| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "crash-reporter/user_collector.h" | 5 #include "crash-reporter/user_collector.h" |
| 6 | 6 |
| 7 #include <grp.h> // For struct group. | 7 #include <grp.h> // For struct group. |
| 8 #include <pcrecpp.h> | 8 #include <pcrecpp.h> |
| 9 #include <pcrecpp.h> | 9 #include <pcrecpp.h> |
| 10 #include <pwd.h> // For struct passwd. | 10 #include <pwd.h> // For struct passwd. |
| 11 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. | 11 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. |
| 12 | 12 |
| 13 #include <string> | 13 #include <string> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/file_util.h" | 16 #include "base/file_util.h" |
| 17 #include "base/logging.h" | 17 #include "base/logging.h" |
| 18 #include "base/string_util.h" | 18 #include "base/string_util.h" |
| 19 #include "crash-reporter/system_logging.h" | 19 #include "chromeos/process.h" |
| 20 #include "chromeos/syslog_logging.h" |
| 20 #include "gflags/gflags.h" | 21 #include "gflags/gflags.h" |
| 21 | 22 |
| 22 #pragma GCC diagnostic ignored "-Wstrict-aliasing" | 23 #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
| 23 DEFINE_bool(core2md_failure, false, "Core2md failure test"); | 24 DEFINE_bool(core2md_failure, false, "Core2md failure test"); |
| 24 DEFINE_bool(directory_failure, false, "Spool directory failure test"); | 25 DEFINE_bool(directory_failure, false, "Spool directory failure test"); |
| 25 DEFINE_string(filter_in, "", | 26 DEFINE_string(filter_in, "", |
| 26 "Ignore all crashes but this for testing"); | 27 "Ignore all crashes but this for testing"); |
| 27 #pragma GCC diagnostic error "-Wstrict-aliasing" | 28 #pragma GCC diagnostic error "-Wstrict-aliasing" |
| 28 | 29 |
| 29 static const char kCollectionErrorSignature[] = | 30 static const char kCollectionErrorSignature[] = |
| (...skipping 18 matching lines...) Expand all Loading... |
| 48 : generate_diagnostics_(false), | 49 : generate_diagnostics_(false), |
| 49 core_pattern_file_(kCorePatternFile), | 50 core_pattern_file_(kCorePatternFile), |
| 50 core_pipe_limit_file_(kCorePipeLimitFile), | 51 core_pipe_limit_file_(kCorePipeLimitFile), |
| 51 initialized_(false) { | 52 initialized_(false) { |
| 52 } | 53 } |
| 53 | 54 |
| 54 void UserCollector::Initialize( | 55 void UserCollector::Initialize( |
| 55 UserCollector::CountCrashFunction count_crash_function, | 56 UserCollector::CountCrashFunction count_crash_function, |
| 56 const std::string &our_path, | 57 const std::string &our_path, |
| 57 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, | 58 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, |
| 58 SystemLogging *logger, | |
| 59 bool generate_diagnostics) { | 59 bool generate_diagnostics) { |
| 60 CrashCollector::Initialize(count_crash_function, | 60 CrashCollector::Initialize(count_crash_function, |
| 61 is_feedback_allowed_function, | 61 is_feedback_allowed_function); |
| 62 logger); | |
| 63 our_path_ = our_path; | 62 our_path_ = our_path; |
| 64 initialized_ = true; | 63 initialized_ = true; |
| 65 generate_diagnostics_ = generate_diagnostics; | 64 generate_diagnostics_ = generate_diagnostics; |
| 66 } | 65 } |
| 67 | 66 |
| 68 UserCollector::~UserCollector() { | 67 UserCollector::~UserCollector() { |
| 69 } | 68 } |
| 70 | 69 |
| 71 std::string UserCollector::GetPattern(bool enabled) const { | 70 std::string UserCollector::GetPattern(bool enabled) const { |
| 72 if (enabled) { | 71 if (enabled) { |
| 73 // Combine the three crash attributes into one parameter to try to reduce | 72 // Combine the three crash attributes into one parameter to try to reduce |
| 74 // the size of the invocation line for crash_reporter since the kernel | 73 // the size of the invocation line for crash_reporter since the kernel |
| 75 // has a fixed-sized (128B) buffer that it will truncate into. Note that | 74 // has a fixed-sized (128B) buffer that it will truncate into. Note that |
| 76 // the kernel does not support quoted arguments in core_pattern. | 75 // the kernel does not support quoted arguments in core_pattern. |
| 77 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str()); | 76 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str()); |
| 78 } else { | 77 } else { |
| 79 return "core"; | 78 return "core"; |
| 80 } | 79 } |
| 81 } | 80 } |
| 82 | 81 |
| 83 bool UserCollector::SetUpInternal(bool enabled) { | 82 bool UserCollector::SetUpInternal(bool enabled) { |
| 84 CHECK(initialized_); | 83 CHECK(initialized_); |
| 85 logger_->LogInfo("%s user crash handling", | 84 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling"; |
| 86 enabled ? "Enabling" : "Disabling"); | 85 |
| 87 if (file_util::WriteFile(FilePath(core_pipe_limit_file_), | 86 if (file_util::WriteFile(FilePath(core_pipe_limit_file_), |
| 88 kCorePipeLimit, | 87 kCorePipeLimit, |
| 89 strlen(kCorePipeLimit)) != | 88 strlen(kCorePipeLimit)) != |
| 90 static_cast<int>(strlen(kCorePipeLimit))) { | 89 static_cast<int>(strlen(kCorePipeLimit))) { |
| 91 logger_->LogError("Unable to write %s", core_pipe_limit_file_.c_str()); | 90 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_; |
| 92 return false; | 91 return false; |
| 93 } | 92 } |
| 94 std::string pattern = GetPattern(enabled); | 93 std::string pattern = GetPattern(enabled); |
| 95 if (file_util::WriteFile(FilePath(core_pattern_file_), | 94 if (file_util::WriteFile(FilePath(core_pattern_file_), |
| 96 pattern.c_str(), | 95 pattern.c_str(), |
| 97 pattern.length()) != | 96 pattern.length()) != |
| 98 static_cast<int>(pattern.length())) { | 97 static_cast<int>(pattern.length())) { |
| 99 logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); | 98 LOG(ERROR) << "Unable to write " << core_pattern_file_; |
| 100 return false; | 99 return false; |
| 101 } | 100 } |
| 102 return true; | 101 return true; |
| 103 } | 102 } |
| 104 | 103 |
| 105 FilePath UserCollector::GetProcessPath(pid_t pid) { | 104 FilePath UserCollector::GetProcessPath(pid_t pid) { |
| 106 return FilePath(StringPrintf("/proc/%d", pid)); | 105 return FilePath(StringPrintf("/proc/%d", pid)); |
| 107 } | 106 } |
| 108 | 107 |
| 109 bool UserCollector::GetSymlinkTarget(const FilePath &symlink, | 108 bool UserCollector::GetSymlinkTarget(const FilePath &symlink, |
| 110 FilePath *target) { | 109 FilePath *target) { |
| 111 int max_size = 32; | 110 int max_size = 32; |
| 112 scoped_array<char> buffer; | 111 scoped_array<char> buffer; |
| 113 while (true) { | 112 while (true) { |
| 114 buffer.reset(new char[max_size + 1]); | 113 buffer.reset(new char[max_size + 1]); |
| 115 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); | 114 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); |
| 116 if (size < 0) { | 115 if (size < 0) { |
| 117 int saved_errno = errno; | 116 int saved_errno = errno; |
| 118 logger_->LogError("Readlink failed on %s with %d", | 117 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with " |
| 119 symlink.value().c_str(), saved_errno); | 118 << saved_errno; |
| 120 return false; | 119 return false; |
| 121 } | 120 } |
| 122 buffer[size] = 0; | 121 buffer[size] = 0; |
| 123 if (size == max_size) { | 122 if (size == max_size) { |
| 124 // Avoid overflow when doubling. | 123 // Avoid overflow when doubling. |
| 125 if (max_size * 2 > max_size) { | 124 if (max_size * 2 > max_size) { |
| 126 max_size *= 2; | 125 max_size *= 2; |
| 127 continue; | 126 continue; |
| 128 } else { | 127 } else { |
| 129 return false; | 128 return false; |
| 130 } | 129 } |
| 131 } | 130 } |
| 132 break; | 131 break; |
| 133 } | 132 } |
| 134 | 133 |
| 135 *target = FilePath(buffer.get()); | 134 *target = FilePath(buffer.get()); |
| 136 return true; | 135 return true; |
| 137 } | 136 } |
| 138 | 137 |
| 139 bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, | 138 bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, |
| 140 std::string *base_name) { | 139 std::string *base_name) { |
| 141 FilePath target; | 140 FilePath target; |
| 142 FilePath process_path = GetProcessPath(pid); | 141 FilePath process_path = GetProcessPath(pid); |
| 143 FilePath exe_path = process_path.Append("exe"); | 142 FilePath exe_path = process_path.Append("exe"); |
| 144 if (!GetSymlinkTarget(exe_path, &target)) { | 143 if (!GetSymlinkTarget(exe_path, &target)) { |
| 145 logger_->LogInfo("GetSymlinkTarget failed - Path %s DirectoryExists: %d", | 144 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value() |
| 146 process_path.value().c_str(), | 145 << " DirectoryExists: " |
| 147 file_util::DirectoryExists(process_path)); | 146 << file_util::DirectoryExists(process_path); |
| 148 // Try to further diagnose exe readlink failure cause. | 147 // Try to further diagnose exe readlink failure cause. |
| 149 struct stat buf; | 148 struct stat buf; |
| 150 int stat_result = stat(exe_path.value().c_str(), &buf); | 149 int stat_result = stat(exe_path.value().c_str(), &buf); |
| 151 int saved_errno = errno; | 150 int saved_errno = errno; |
| 152 if (stat_result < 0) { | 151 if (stat_result < 0) { |
| 153 logger_->LogInfo("stat %s failed: %d %d", exe_path.value().c_str(), | 152 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result |
| 154 stat_result, saved_errno); | 153 << " " << saved_errno; |
| 155 } else { | 154 } else { |
| 156 logger_->LogInfo("stat %s succeeded: st_mode=%d", | 155 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode=" |
| 157 exe_path.value().c_str(), buf.st_mode); | 156 << buf.st_mode; |
| 158 } | 157 } |
| 159 return false; | 158 return false; |
| 160 } | 159 } |
| 161 *base_name = target.BaseName().value(); | 160 *base_name = target.BaseName().value(); |
| 162 return true; | 161 return true; |
| 163 } | 162 } |
| 164 | 163 |
| 165 bool UserCollector::GetIdFromStatus(const char *prefix, | 164 bool UserCollector::GetIdFromStatus(const char *prefix, |
| 166 IdKind kind, | 165 IdKind kind, |
| 167 const std::string &status_contents, | 166 const std::string &status_contents, |
| (...skipping 23 matching lines...) Expand all Loading... |
| 191 char *end_number = NULL; | 190 char *end_number = NULL; |
| 192 *id = strtol(number, &end_number, 10); | 191 *id = strtol(number, &end_number, 10); |
| 193 if (*end_number != '\0') | 192 if (*end_number != '\0') |
| 194 return false; | 193 return false; |
| 195 return true; | 194 return true; |
| 196 } | 195 } |
| 197 | 196 |
| 198 void UserCollector::EnqueueCollectionErrorLog(pid_t pid, | 197 void UserCollector::EnqueueCollectionErrorLog(pid_t pid, |
| 199 const std::string &exec) { | 198 const std::string &exec) { |
| 200 FilePath crash_path; | 199 FilePath crash_path; |
| 201 logger_->LogInfo("Writing conversion problems as separate crash report."); | 200 LOG(INFO) << "Writing conversion problems as separate crash report."; |
| 202 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { | 201 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { |
| 203 logger_->LogError("Could not even get log directory; out of space?"); | 202 LOG(ERROR) << "Could not even get log directory; out of space?"; |
| 204 return; | 203 return; |
| 205 } | 204 } |
| 206 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); | 205 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); |
| 207 std::string error_log = logger_->get_accumulator(); | 206 std::string error_log = syslog_logging::GetAccumulatedLogging(); |
| 208 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); | 207 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); |
| 209 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature, | 208 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature, |
| 210 diag_log_path)) { | 209 diag_log_path)) { |
| 211 // We load the contents of diag_log into memory and append it to | 210 // We load the contents of diag_log into memory and append it to |
| 212 // the error log. We cannot just append to files because we need | 211 // the error log. We cannot just append to files because we need |
| 213 // to always create new files to prevent attack. | 212 // to always create new files to prevent attack. |
| 214 std::string diag_log_contents; | 213 std::string diag_log_contents; |
| 215 file_util::ReadFileToString(diag_log_path, &diag_log_contents); | 214 file_util::ReadFileToString(diag_log_path, &diag_log_contents); |
| 216 error_log.append(diag_log_contents); | 215 error_log.append(diag_log_contents); |
| 217 file_util::Delete(diag_log_path, false); | 216 file_util::Delete(diag_log_path, false); |
| 218 } | 217 } |
| 219 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); | 218 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); |
| 220 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); | 219 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); |
| 221 // We must use WriteNewFile instead of file_util::WriteFile as we do | 220 // We must use WriteNewFile instead of file_util::WriteFile as we do |
| 222 // not want to write with root access to a symlink that an attacker | 221 // not want to write with root access to a symlink that an attacker |
| 223 // might have created. | 222 // might have created. |
| 224 WriteNewFile(log_path, error_log.data(), error_log.length()); | 223 WriteNewFile(log_path, error_log.data(), error_log.length()); |
| 225 AddCrashMetaData("sig", kCollectionErrorSignature); | 224 AddCrashMetaData("sig", kCollectionErrorSignature); |
| 226 WriteCrashMetaData(meta_path, exec, log_path.value()); | 225 WriteCrashMetaData(meta_path, exec, log_path.value()); |
| 227 } | 226 } |
| 228 | 227 |
| 229 bool UserCollector::CopyOffProcFiles(pid_t pid, | 228 bool UserCollector::CopyOffProcFiles(pid_t pid, |
| 230 const FilePath &container_dir) { | 229 const FilePath &container_dir) { |
| 231 if (!file_util::CreateDirectory(container_dir)) { | 230 if (!file_util::CreateDirectory(container_dir)) { |
| 232 logger_->LogError("Could not create %s", | 231 LOG(ERROR) << "Could not create " << container_dir.value().c_str(); |
| 233 container_dir.value().c_str()); | |
| 234 return false; | 232 return false; |
| 235 } | 233 } |
| 236 FilePath process_path = GetProcessPath(pid); | 234 FilePath process_path = GetProcessPath(pid); |
| 237 if (!file_util::PathExists(process_path)) { | 235 if (!file_util::PathExists(process_path)) { |
| 238 logger_->LogError("Path %s does not exist", process_path.value().c_str()); | 236 LOG(ERROR) << "Path " << process_path.value() << " does not exist"; |
| 239 return false; | 237 return false; |
| 240 } | 238 } |
| 241 static const char *proc_files[] = { | 239 static const char *proc_files[] = { |
| 242 "auxv", | 240 "auxv", |
| 243 "cmdline", | 241 "cmdline", |
| 244 "environ", | 242 "environ", |
| 245 "maps", | 243 "maps", |
| 246 "status" | 244 "status" |
| 247 }; | 245 }; |
| 248 for (unsigned i = 0; i < arraysize(proc_files); ++i) { | 246 for (unsigned i = 0; i < arraysize(proc_files); ++i) { |
| 249 if (!file_util::CopyFile(process_path.Append(proc_files[i]), | 247 if (!file_util::CopyFile(process_path.Append(proc_files[i]), |
| 250 container_dir.Append(proc_files[i]))) { | 248 container_dir.Append(proc_files[i]))) { |
| 251 logger_->LogError("Could not copy %s file", proc_files[i]); | 249 LOG(ERROR) << "Could not copy " << proc_files[i] << " file"; |
| 252 return false; | 250 return false; |
| 253 } | 251 } |
| 254 } | 252 } |
| 255 return true; | 253 return true; |
| 256 } | 254 } |
| 257 | 255 |
| 258 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, | 256 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, |
| 259 FilePath *crash_file_path, | 257 FilePath *crash_file_path, |
| 260 bool *out_of_capacity) { | 258 bool *out_of_capacity) { |
| 261 FilePath process_path = GetProcessPath(pid); | 259 FilePath process_path = GetProcessPath(pid); |
| 262 std::string status; | 260 std::string status; |
| 263 if (FLAGS_directory_failure) { | 261 if (FLAGS_directory_failure) { |
| 264 logger_->LogError("Purposefully failing to create spool directory"); | 262 LOG(ERROR) << "Purposefully failing to create spool directory"; |
| 265 return false; | 263 return false; |
| 266 } | 264 } |
| 267 if (!file_util::ReadFileToString(process_path.Append("status"), | 265 if (!file_util::ReadFileToString(process_path.Append("status"), |
| 268 &status)) { | 266 &status)) { |
| 269 logger_->LogError("Could not read status file"); | 267 LOG(ERROR) << "Could not read status file"; |
| 270 logger_->LogInfo("Path %s DirectoryExists: %d", | 268 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: " |
| 271 process_path.value().c_str(), | 269 << file_util::DirectoryExists(process_path); |
| 272 file_util::DirectoryExists(process_path)); | |
| 273 return false; | 270 return false; |
| 274 } | 271 } |
| 275 int process_euid; | 272 int process_euid; |
| 276 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { | 273 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { |
| 277 logger_->LogError("Could not find euid in status file"); | 274 LOG(ERROR) << "Could not find euid in status file"; |
| 278 return false; | 275 return false; |
| 279 } | 276 } |
| 280 if (!GetCreatedCrashDirectoryByEuid(process_euid, | 277 if (!GetCreatedCrashDirectoryByEuid(process_euid, |
| 281 crash_file_path, | 278 crash_file_path, |
| 282 out_of_capacity)) { | 279 out_of_capacity)) { |
| 283 logger_->LogError("Could not create crash directory"); | 280 LOG(ERROR) << "Could not create crash directory"; |
| 284 return false; | 281 return false; |
| 285 } | 282 } |
| 286 return true; | 283 return true; |
| 287 } | 284 } |
| 288 | 285 |
| 289 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { | 286 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { |
| 290 // Copy off all stdin to a core file. | 287 // Copy off all stdin to a core file. |
| 291 FilePath stdin_path("/dev/fd/0"); | 288 FilePath stdin_path("/dev/fd/0"); |
| 292 if (file_util::CopyFile(stdin_path, core_path)) { | 289 if (file_util::CopyFile(stdin_path, core_path)) { |
| 293 return true; | 290 return true; |
| 294 } | 291 } |
| 295 | 292 |
| 296 logger_->LogError("Could not write core file"); | 293 LOG(ERROR) << "Could not write core file"; |
| 297 // If the file system was full, make sure we remove any remnants. | 294 // If the file system was full, make sure we remove any remnants. |
| 298 file_util::Delete(core_path, false); | 295 file_util::Delete(core_path, false); |
| 299 return false; | 296 return false; |
| 300 } | 297 } |
| 301 | 298 |
| 302 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, | 299 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, |
| 303 const FilePath &procfs_directory, | 300 const FilePath &procfs_directory, |
| 304 const FilePath &minidump_path, | 301 const FilePath &minidump_path, |
| 305 const FilePath &temp_directory) { | 302 const FilePath &temp_directory) { |
| 306 FilePath output_path = temp_directory.Append("output"); | 303 FilePath output_path = temp_directory.Append("output"); |
| 307 std::vector<const char *> core2md_arguments; | 304 ProcessImpl core2md; |
| 308 core2md_arguments.push_back(kCoreToMinidumpConverterPath); | 305 core2md.SetOutput(output_path.value()); |
| 309 core2md_arguments.push_back(core_path.value().c_str()); | 306 core2md.AddArg(kCoreToMinidumpConverterPath); |
| 310 core2md_arguments.push_back(procfs_directory.value().c_str()); | 307 core2md.AddArg(core_path.value()); |
| 311 core2md_arguments.push_back(minidump_path.value().c_str()); | 308 core2md.AddArg(procfs_directory.value()); |
| 312 | 309 |
| 313 if (FLAGS_core2md_failure) { | 310 if (!FLAGS_core2md_failure) { |
| 311 core2md.AddArg(minidump_path.value()); |
| 312 } else { |
| 314 // To test how core2md errors are propagaged, cause an error | 313 // To test how core2md errors are propagaged, cause an error |
| 315 // by forgetting a required argument. | 314 // by forgetting a required argument. |
| 316 core2md_arguments.pop_back(); | |
| 317 } | 315 } |
| 318 | 316 |
| 319 int errorlevel = ForkExecAndPipe(core2md_arguments, | 317 int errorlevel = core2md.Run(); |
| 320 output_path.value().c_str()); | |
| 321 | 318 |
| 322 std::string output; | 319 std::string output; |
| 323 file_util::ReadFileToString(output_path, &output); | 320 file_util::ReadFileToString(output_path, &output); |
| 324 if (errorlevel != 0) { | 321 if (errorlevel != 0) { |
| 325 logger_->LogError("Problem during %s [result=%d]: %s", | 322 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath |
| 326 kCoreToMinidumpConverterPath, | 323 << " [result=" << errorlevel << "]: " << output; |
| 327 errorlevel, | |
| 328 output.c_str()); | |
| 329 return false; | 324 return false; |
| 330 } | 325 } |
| 331 | 326 |
| 332 if (!file_util::PathExists(minidump_path)) { | 327 if (!file_util::PathExists(minidump_path)) { |
| 333 logger_->LogError("Minidump file %s was not created", | 328 LOG(ERROR) << "Minidump file " << minidump_path.value() |
| 334 minidump_path.value().c_str()); | 329 << " was not created"; |
| 335 return false; | 330 return false; |
| 336 } | 331 } |
| 337 return true; | 332 return true; |
| 338 } | 333 } |
| 339 | 334 |
| 340 bool UserCollector::ConvertCoreToMinidump(pid_t pid, | 335 bool UserCollector::ConvertCoreToMinidump(pid_t pid, |
| 341 const FilePath &container_dir, | 336 const FilePath &container_dir, |
| 342 const FilePath &core_path, | 337 const FilePath &core_path, |
| 343 const FilePath &minidump_path) { | 338 const FilePath &minidump_path) { |
| 344 if (!CopyOffProcFiles(pid, container_dir)) { | 339 if (!CopyOffProcFiles(pid, container_dir)) { |
| 345 return false; | 340 return false; |
| 346 } | 341 } |
| 347 | 342 |
| 348 if (!CopyStdinToCoreFile(core_path)) { | 343 if (!CopyStdinToCoreFile(core_path)) { |
| 349 return false; | 344 return false; |
| 350 } | 345 } |
| 351 | 346 |
| 352 bool conversion_result = RunCoreToMinidump( | 347 bool conversion_result = RunCoreToMinidump( |
| 353 core_path, | 348 core_path, |
| 354 container_dir, // procfs directory | 349 container_dir, // procfs directory |
| 355 minidump_path, | 350 minidump_path, |
| 356 container_dir); // temporary directory | 351 container_dir); // temporary directory |
| 357 | 352 |
| 358 if (conversion_result) { | 353 if (conversion_result) { |
| 359 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); | 354 LOG(INFO) << "Stored minidump to " << minidump_path.value(); |
| 360 } | 355 } |
| 361 | 356 |
| 362 return conversion_result; | 357 return conversion_result; |
| 363 } | 358 } |
| 364 | 359 |
| 365 bool UserCollector::ConvertAndEnqueueCrash(int pid, | 360 bool UserCollector::ConvertAndEnqueueCrash(int pid, |
| 366 const std::string &exec, | 361 const std::string &exec, |
| 367 bool *out_of_capacity) { | 362 bool *out_of_capacity) { |
| 368 FilePath crash_path; | 363 FilePath crash_path; |
| 369 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { | 364 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { |
| 370 logger_->LogError("Unable to find/create process-specific crash path"); | 365 LOG(ERROR) << "Unable to find/create process-specific crash path"; |
| 371 return false; | 366 return false; |
| 372 } | 367 } |
| 373 | 368 |
| 374 // Directory like /tmp/crash_reporter.1234 which contains the | 369 // Directory like /tmp/crash_reporter.1234 which contains the |
| 375 // procfs entries and other temporary files used during conversion. | 370 // procfs entries and other temporary files used during conversion. |
| 376 FilePath container_dir = FilePath("/tmp").Append( | 371 FilePath container_dir = FilePath("/tmp").Append( |
| 377 StringPrintf("crash_reporter.%d", pid)); | 372 StringPrintf("crash_reporter.%d", pid)); |
| 378 // Delete a pre-existing directory from crash reporter that may have | 373 // Delete a pre-existing directory from crash reporter that may have |
| 379 // been left around for diagnostics from a failed conversion attempt. | 374 // been left around for diagnostics from a failed conversion attempt. |
| 380 // If we don't, existing files can cause forking to fail. | 375 // If we don't, existing files can cause forking to fail. |
| 381 file_util::Delete(container_dir, true); | 376 file_util::Delete(container_dir, true); |
| 382 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); | 377 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); |
| 383 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); | 378 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); |
| 384 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); | 379 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); |
| 385 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); | 380 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); |
| 386 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); | 381 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); |
| 387 | 382 |
| 388 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path)) | 383 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path)) |
| 389 AddCrashMetaData("log", log_path.value()); | 384 AddCrashMetaData("log", log_path.value()); |
| 390 | 385 |
| 391 if (!ConvertCoreToMinidump(pid, container_dir, core_path, | 386 if (!ConvertCoreToMinidump(pid, container_dir, core_path, |
| 392 minidump_path)) { | 387 minidump_path)) { |
| 393 logger_->LogInfo("Leaving core file at %s due to conversion error", | 388 LOG(INFO) << "Leaving core file at " << core_path.value() |
| 394 core_path.value().c_str()); | 389 << " due to conversion error"; |
| 395 return false; | 390 return false; |
| 396 } | 391 } |
| 397 | 392 |
| 398 // Here we commit to sending this file. We must not return false | 393 // Here we commit to sending this file. We must not return false |
| 399 // after this point or we will generate a log report as well as a | 394 // after this point or we will generate a log report as well as a |
| 400 // crash report. | 395 // crash report. |
| 401 WriteCrashMetaData(meta_path, | 396 WriteCrashMetaData(meta_path, |
| 402 exec, | 397 exec, |
| 403 minidump_path.value()); | 398 minidump_path.value()); |
| 404 | 399 |
| 405 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { | 400 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { |
| 406 file_util::Delete(core_path, false); | 401 file_util::Delete(core_path, false); |
| 407 } else { | 402 } else { |
| 408 logger_->LogInfo("Leaving core file at %s due to developer image", | 403 LOG(INFO) << "Leaving core file at " << core_path.value() |
| 409 core_path.value().c_str()); | 404 << " due to developer image"; |
| 410 } | 405 } |
| 411 | 406 |
| 412 file_util::Delete(container_dir, true); | 407 file_util::Delete(container_dir, true); |
| 413 return true; | 408 return true; |
| 414 } | 409 } |
| 415 | 410 |
| 416 bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, | 411 bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, |
| 417 pid_t *pid, int *signal, | 412 pid_t *pid, int *signal, |
| 418 std::string *kernel_supplied_name) { | 413 std::string *kernel_supplied_name) { |
| 419 pcrecpp::RE re("(\\d+):(\\d+):(.*)"); | 414 pcrecpp::RE re("(\\d+):(\\d+):(.*)"); |
| 420 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); | 415 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); |
| 421 } | 416 } |
| 422 | 417 |
| 423 bool UserCollector::HandleCrash(const std::string &crash_attributes, | 418 bool UserCollector::HandleCrash(const std::string &crash_attributes, |
| 424 const char *force_exec) { | 419 const char *force_exec) { |
| 425 CHECK(initialized_); | 420 CHECK(initialized_); |
| 426 int pid = 0; | 421 int pid = 0; |
| 427 int signal = 0; | 422 int signal = 0; |
| 428 std::string kernel_supplied_name; | 423 std::string kernel_supplied_name; |
| 429 | 424 |
| 430 if (!ParseCrashAttributes(crash_attributes, &pid, &signal, | 425 if (!ParseCrashAttributes(crash_attributes, &pid, &signal, |
| 431 &kernel_supplied_name)) { | 426 &kernel_supplied_name)) { |
| 432 logger_->LogError("Invalid parameter: --user=%s", crash_attributes.c_str()); | 427 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes; |
| 433 return false; | 428 return false; |
| 434 } | 429 } |
| 435 | 430 |
| 436 std::string exec; | 431 std::string exec; |
| 437 if (force_exec) { | 432 if (force_exec) { |
| 438 exec.assign(force_exec); | 433 exec.assign(force_exec); |
| 439 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { | 434 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { |
| 440 // If we cannot find the exec name, use the kernel supplied name. | 435 // If we cannot find the exec name, use the kernel supplied name. |
| 441 // We don't always use the kernel's since it truncates the name to | 436 // We don't always use the kernel's since it truncates the name to |
| 442 // 16 characters. | 437 // 16 characters. |
| 443 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str()); | 438 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str()); |
| 444 } | 439 } |
| 445 | 440 |
| 446 // Allow us to test the crash reporting mechanism successfully even if | 441 // Allow us to test the crash reporting mechanism successfully even if |
| 447 // other parts of the system crash. | 442 // other parts of the system crash. |
| 448 if (!FLAGS_filter_in.empty() && | 443 if (!FLAGS_filter_in.empty() && |
| 449 (FLAGS_filter_in == "none" || | 444 (FLAGS_filter_in == "none" || |
| 450 FLAGS_filter_in != exec)) { | 445 FLAGS_filter_in != exec)) { |
| 451 // We use a different format message to make it more obvious in tests | 446 // We use a different format message to make it more obvious in tests |
| 452 // which crashes are test generated and which are real. | 447 // which crashes are test generated and which are real. |
| 453 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s.", | 448 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while " |
| 454 exec.c_str(), pid, FLAGS_filter_in.c_str()); | 449 << "filter_in=" << FLAGS_filter_in << "."; |
| 455 return true; | 450 return true; |
| 456 } | 451 } |
| 457 | 452 |
| 458 bool feedback = is_feedback_allowed_function_(); | 453 bool feedback = is_feedback_allowed_function_(); |
| 459 const char *handling_string = "handling"; | 454 const char *handling_string = "handling"; |
| 460 if (!feedback) { | 455 if (!feedback) { |
| 461 handling_string = "ignoring - no consent"; | 456 handling_string = "ignoring - no consent"; |
| 462 } | 457 } |
| 463 | 458 |
| 464 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome | 459 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome |
| 465 // crashes towards user crashes, so user crashes really mean non-Chrome | 460 // crashes towards user crashes, so user crashes really mean non-Chrome |
| 466 // user-space crashes. | 461 // user-space crashes. |
| 467 if (exec == "chrome" || exec == "supplied_chrome") { | 462 if (exec == "chrome" || exec == "supplied_chrome") { |
| 468 feedback = false; | 463 feedback = false; |
| 469 handling_string = "ignoring - chrome crash"; | 464 handling_string = "ignoring - chrome crash"; |
| 470 } | 465 } |
| 471 | 466 |
| 472 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", | 467 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid |
| 473 exec.c_str(), pid, signal, handling_string); | 468 << "] sig " << signal << " (" << handling_string << ")"; |
| 474 | 469 |
| 475 if (feedback) { | 470 if (feedback) { |
| 476 count_crash_function_(); | 471 count_crash_function_(); |
| 477 | 472 |
| 478 if (generate_diagnostics_) { | 473 if (generate_diagnostics_) { |
| 479 bool out_of_capacity = false; | 474 bool out_of_capacity = false; |
| 480 bool convert_and_enqueue_result = | 475 bool convert_and_enqueue_result = |
| 481 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); | 476 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); |
| 482 if (!convert_and_enqueue_result) { | 477 if (!convert_and_enqueue_result) { |
| 483 if (!out_of_capacity) | 478 if (!out_of_capacity) |
| 484 EnqueueCollectionErrorLog(pid, exec); | 479 EnqueueCollectionErrorLog(pid, exec); |
| 485 return false; | 480 return false; |
| 486 } | 481 } |
| 487 } | 482 } |
| 488 } | 483 } |
| 489 | 484 |
| 490 return true; | 485 return true; |
| 491 } | 486 } |
| OLD | NEW |