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 |