Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(648)

Side by Side Diff: user_collector.cc

Issue 6297004: crash-reporter: Add diagnostics to help diagnose failures in the wild (Closed) Base URL: http://git.chromium.org/git/crash-reporter.git@master
Patch Set: log bad params instead of check failing Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 <pwd.h> // For struct passwd. 8 #include <pwd.h> // For struct passwd.
9 #include <pcrecpp.h>
petkov 2011/01/24 18:36:04 sort
kmixter1 2011/01/25 21:28:07 Done.
9 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. 10 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
10 11
11 #include <string> 12 #include <string>
12 #include <vector> 13 #include <vector>
13 14
14 #include "base/file_util.h" 15 #include "base/file_util.h"
15 #include "base/logging.h" 16 #include "base/logging.h"
16 #include "base/string_util.h" 17 #include "base/string_util.h"
17 #include "crash-reporter/system_logging.h" 18 #include "crash-reporter/system_logging.h"
18 #include "gflags/gflags.h" 19 #include "gflags/gflags.h"
19 20
20 #pragma GCC diagnostic ignored "-Wstrict-aliasing" 21 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
21 DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); 22 DEFINE_bool(core2md_failure, false, "Core2md failure test");
22 DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); 23 DEFINE_bool(directory_failure, false, "Spool directory failure test");
23 DEFINE_string(filter_in, "", 24 DEFINE_string(filter_in, "",
24 "Ignore all crashes but this for testing"); 25 "Ignore all crashes but this for testing");
25 #pragma GCC diagnostic error "-Wstrict-aliasing" 26 #pragma GCC diagnostic error "-Wstrict-aliasing"
26 27
27 static const char kCollectionErrorSignature[] = 28 static const char kCollectionErrorSignature[] =
28 "crash_reporter-user-collection"; 29 "crash_reporter-user-collection";
29 // This procfs file is used to cause kernel core file writing to 30 // This procfs file is used to cause kernel core file writing to
30 // instead pipe the core file into a user space process. See 31 // instead pipe the core file into a user space process. See
31 // core(5) man page. 32 // core(5) man page.
32 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; 33 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
(...skipping 28 matching lines...) Expand all
61 our_path_ = our_path; 62 our_path_ = our_path;
62 initialized_ = true; 63 initialized_ = true;
63 generate_diagnostics_ = generate_diagnostics; 64 generate_diagnostics_ = generate_diagnostics;
64 } 65 }
65 66
66 UserCollector::~UserCollector() { 67 UserCollector::~UserCollector() {
67 } 68 }
68 69
69 std::string UserCollector::GetPattern(bool enabled) const { 70 std::string UserCollector::GetPattern(bool enabled) const {
70 if (enabled) { 71 if (enabled) {
71 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); 72 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
petkov 2011/01/24 18:36:04 I guess no concern about %%e containing spaces?
petkov 2011/01/24 18:36:04 add a comment why you're combining the values into
kmixter1 2011/01/25 21:28:07 It's annoying but the kernel doesn't support quoti
kmixter1 2011/01/25 21:28:07 Done.
72 } else { 73 } else {
73 return "core"; 74 return "core";
74 } 75 }
75 } 76 }
76 77
77 bool UserCollector::SetUpInternal(bool enabled) { 78 bool UserCollector::SetUpInternal(bool enabled) {
78 CHECK(initialized_); 79 CHECK(initialized_);
79 logger_->LogInfo("%s user crash handling", 80 logger_->LogInfo("%s user crash handling",
80 enabled ? "Enabling" : "Disabling"); 81 enabled ? "Enabling" : "Disabling");
81 if (file_util::WriteFile(FilePath(core_pipe_limit_file_), 82 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 return false; 163 return false;
163 } 164 }
164 const char *number = ids[kind].c_str(); 165 const char *number = ids[kind].c_str();
165 char *end_number = NULL; 166 char *end_number = NULL;
166 *id = strtol(number, &end_number, 10); 167 *id = strtol(number, &end_number, 10);
167 if (*end_number != '\0') 168 if (*end_number != '\0')
168 return false; 169 return false;
169 return true; 170 return true;
170 } 171 }
171 172
172 void UserCollector::LogCollectionError(const std::string &error_message) {
173 error_log_.append(error_message.c_str());
174 error_log_.append("\n");
175 logger_->LogError(error_message.c_str());
176 }
177
178 void UserCollector::EnqueueCollectionErrorLog(pid_t pid, 173 void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
179 const std::string &exec) { 174 const std::string &exec) {
180 FilePath crash_path; 175 FilePath crash_path;
181 logger_->LogInfo("Writing conversion problems as separate crash report."); 176 logger_->LogInfo("Writing conversion problems as separate crash report.");
182 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { 177 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
183 logger_->LogError("Could not even get log directory; out of space?"); 178 logger_->LogError("Could not even get log directory; out of space?");
184 return; 179 return;
185 } 180 }
186 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); 181 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
182 std::string error_log = logger_->get_accumulator();
183 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
184 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature,
185 diag_log_path)) {
186 // We load the contents of diag_log into memory and append it to
187 // the error log. We cannot just append to files because we need
188 // to always create new files to prevent attack.
189 std::string diag_log_contents;
190 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
191 error_log.append(diag_log_contents);
192 file_util::Delete(diag_log_path, false);
193 }
187 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); 194 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
188 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 195 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
189 // We must use WriteNewFile instead of file_util::WriteFile as we do 196 // We must use WriteNewFile instead of file_util::WriteFile as we do
190 // not want to write with root access to a symlink that an attacker 197 // not want to write with root access to a symlink that an attacker
191 // might have created. 198 // might have created.
192 WriteNewFile(log_path, error_log_.data(), error_log_.length()); 199 WriteNewFile(log_path, error_log.data(), error_log.length());
193 AddCrashMetaData("sig", kCollectionErrorSignature); 200 AddCrashMetaData("sig", kCollectionErrorSignature);
194 WriteCrashMetaData(meta_path, exec, log_path.value()); 201 WriteCrashMetaData(meta_path, exec, log_path.value());
195 } 202 }
196 203
197 bool UserCollector::CopyOffProcFiles(pid_t pid, 204 bool UserCollector::CopyOffProcFiles(pid_t pid,
198 const FilePath &container_dir) { 205 const FilePath &container_dir) {
199 if (!file_util::CreateDirectory(container_dir)) { 206 if (!file_util::CreateDirectory(container_dir)) {
200 LogCollectionError(StringPrintf("Could not create %s", 207 logger_->LogError("Could not create %s",
201 container_dir.value().c_str())); 208 container_dir.value().c_str());
202 return false; 209 return false;
203 } 210 }
204 FilePath process_path = GetProcessPath(pid); 211 FilePath process_path = GetProcessPath(pid);
205 if (!file_util::PathExists(process_path)) { 212 if (!file_util::PathExists(process_path)) {
206 LogCollectionError(StringPrintf("Path %s does not exist", 213 logger_->LogError("Path %s does not exist", process_path.value().c_str());
207 process_path.value().c_str()));
208 return false; 214 return false;
209 } 215 }
210 static const char *proc_files[] = { 216 static const char *proc_files[] = {
211 "auxv", 217 "auxv",
212 "cmdline", 218 "cmdline",
213 "environ", 219 "environ",
214 "maps", 220 "maps",
215 "status" 221 "status"
216 }; 222 };
217 for (unsigned i = 0; i < arraysize(proc_files); ++i) { 223 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
218 if (!file_util::CopyFile(process_path.Append(proc_files[i]), 224 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
219 container_dir.Append(proc_files[i]))) { 225 container_dir.Append(proc_files[i]))) {
220 LogCollectionError(StringPrintf("Could not copy %s file", 226 logger_->LogError("Could not copy %s file", proc_files[i]);
221 proc_files[i]));
222 return false; 227 return false;
223 } 228 }
224 } 229 }
225 return true; 230 return true;
226 } 231 }
227 232
228 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, 233 bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
229 FilePath *crash_file_path, 234 FilePath *crash_file_path,
230 bool *out_of_capacity) { 235 bool *out_of_capacity) {
231 FilePath process_path = GetProcessPath(pid); 236 FilePath process_path = GetProcessPath(pid);
232 std::string status; 237 std::string status;
233 if (FLAGS_directory_failure_test) { 238 if (FLAGS_directory_failure) {
234 LogCollectionError("Purposefully failing to create spool directory"); 239 logger_->LogError("Purposefully failing to create spool directory");
235 return false; 240 return false;
236 } 241 }
237 if (!file_util::ReadFileToString(process_path.Append("status"), 242 if (!file_util::ReadFileToString(process_path.Append("status"),
238 &status)) { 243 &status)) {
239 LogCollectionError("Could not read status file"); 244 logger_->LogError("Could not read status file");
245 logger_->LogInfo("Path %s FileExists: %d",
246 process_path.value().c_str(),
247 file_util::DirectoryExists(process_path));
240 return false; 248 return false;
241 } 249 }
242 int process_euid; 250 int process_euid;
243 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { 251 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
244 LogCollectionError("Could not find euid in status file"); 252 logger_->LogError("Could not find euid in status file");
245 return false; 253 return false;
246 } 254 }
247 if (!GetCreatedCrashDirectoryByEuid(process_euid, 255 if (!GetCreatedCrashDirectoryByEuid(process_euid,
248 crash_file_path, 256 crash_file_path,
249 out_of_capacity)) { 257 out_of_capacity)) {
250 LogCollectionError("Could not create crash directory"); 258 logger_->LogError("Could not create crash directory");
251 return false; 259 return false;
252 } 260 }
253 return true; 261 return true;
254 } 262 }
255 263
256 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { 264 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
257 // Copy off all stdin to a core file. 265 // Copy off all stdin to a core file.
258 FilePath stdin_path("/dev/fd/0"); 266 FilePath stdin_path("/dev/fd/0");
259 if (file_util::CopyFile(stdin_path, core_path)) { 267 if (file_util::CopyFile(stdin_path, core_path)) {
260 return true; 268 return true;
261 } 269 }
262 270
263 LogCollectionError("Could not write core file"); 271 logger_->LogError("Could not write core file");
264 // If the file system was full, make sure we remove any remnants. 272 // If the file system was full, make sure we remove any remnants.
265 file_util::Delete(core_path, false); 273 file_util::Delete(core_path, false);
266 return false; 274 return false;
267 } 275 }
268 276
269 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, 277 bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
270 const FilePath &procfs_directory, 278 const FilePath &procfs_directory,
271 const FilePath &minidump_path, 279 const FilePath &minidump_path,
272 const FilePath &temp_directory) { 280 const FilePath &temp_directory) {
273 FilePath output_path = temp_directory.Append("output"); 281 FilePath output_path = temp_directory.Append("output");
274 std::vector<const char *> core2md_arguments; 282 std::vector<const char *> core2md_arguments;
275 core2md_arguments.push_back(kCoreToMinidumpConverterPath); 283 core2md_arguments.push_back(kCoreToMinidumpConverterPath);
276 core2md_arguments.push_back(core_path.value().c_str()); 284 core2md_arguments.push_back(core_path.value().c_str());
277 core2md_arguments.push_back(procfs_directory.value().c_str()); 285 core2md_arguments.push_back(procfs_directory.value().c_str());
278 core2md_arguments.push_back(minidump_path.value().c_str()); 286 core2md_arguments.push_back(minidump_path.value().c_str());
279 287
280 if (FLAGS_core2md_failure_test) { 288 if (FLAGS_core2md_failure) {
281 // To test how core2md errors are propagaged, cause an error 289 // To test how core2md errors are propagaged, cause an error
282 // by forgetting a required argument. 290 // by forgetting a required argument.
283 core2md_arguments.pop_back(); 291 core2md_arguments.pop_back();
284 } 292 }
285 293
286 int errorlevel = ForkExecAndPipe(core2md_arguments, 294 int errorlevel = ForkExecAndPipe(core2md_arguments,
287 output_path.value().c_str()); 295 output_path.value().c_str());
288 296
289 std::string output; 297 std::string output;
290 file_util::ReadFileToString(output_path, &output); 298 file_util::ReadFileToString(output_path, &output);
291 if (errorlevel != 0) { 299 if (errorlevel != 0) {
292 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", 300 logger_->LogError("Problem during %s [result=%d]: %s",
293 kCoreToMinidumpConverterPath, 301 kCoreToMinidumpConverterPath,
294 errorlevel, 302 errorlevel,
295 output.c_str())); 303 output.c_str());
296 return false; 304 return false;
297 } 305 }
298 306
299 if (!file_util::PathExists(minidump_path)) { 307 if (!file_util::PathExists(minidump_path)) {
300 LogCollectionError(StringPrintf("Minidump file %s was not created", 308 logger_->LogError("Minidump file %s was not created",
301 minidump_path.value().c_str())); 309 minidump_path.value().c_str());
302 return false; 310 return false;
303 } 311 }
304 return true; 312 return true;
305 } 313 }
306 314
307 bool UserCollector::ConvertCoreToMinidump(pid_t pid, 315 bool UserCollector::ConvertCoreToMinidump(pid_t pid,
308 const FilePath &container_dir, 316 const FilePath &container_dir,
309 const FilePath &core_path, 317 const FilePath &core_path,
310 const FilePath &minidump_path) { 318 const FilePath &minidump_path) {
311 if (!CopyOffProcFiles(pid, container_dir)) { 319 if (!CopyOffProcFiles(pid, container_dir)) {
(...skipping 15 matching lines...) Expand all
327 } 335 }
328 336
329 return conversion_result; 337 return conversion_result;
330 } 338 }
331 339
332 bool UserCollector::ConvertAndEnqueueCrash(int pid, 340 bool UserCollector::ConvertAndEnqueueCrash(int pid,
333 const std::string &exec, 341 const std::string &exec,
334 bool *out_of_capacity) { 342 bool *out_of_capacity) {
335 FilePath crash_path; 343 FilePath crash_path;
336 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { 344 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
337 LogCollectionError("Unable to find/create process-specific crash path"); 345 logger_->LogError("Unable to find/create process-specific crash path");
338 return false; 346 return false;
339 } 347 }
340 348
341 // Directory like /tmp/crash_reporter.1234 which contains the 349 // Directory like /tmp/crash_reporter.1234 which contains the
342 // procfs entries and other temporary files used during conversion. 350 // procfs entries and other temporary files used during conversion.
343 FilePath container_dir = FilePath("/tmp").Append( 351 FilePath container_dir = FilePath("/tmp").Append(
344 StringPrintf("crash_reporter.%d", pid)); 352 StringPrintf("crash_reporter.%d", pid));
345 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); 353 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
346 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); 354 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
347 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 355 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
(...skipping 21 matching lines...) Expand all
369 file_util::Delete(core_path, false); 377 file_util::Delete(core_path, false);
370 } else { 378 } else {
371 logger_->LogInfo("Leaving core file at %s due to developer image", 379 logger_->LogInfo("Leaving core file at %s due to developer image",
372 core_path.value().c_str()); 380 core_path.value().c_str());
373 } 381 }
374 382
375 file_util::Delete(container_dir, true); 383 file_util::Delete(container_dir, true);
376 return true; 384 return true;
377 } 385 }
378 386
379 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { 387 bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
388 pid_t *pid, int *signal,
389 std::string *kernel_supplied_name) {
390 pcrecpp::RE re("(\\d+):(\\d+):(.*)");
391 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
392 }
393
394 bool UserCollector::HandleCrash(const std::string &crash_attributes,
395 const char *force_exec) {
380 CHECK(initialized_); 396 CHECK(initialized_);
397 int pid = 0;
398 int signal = 0;
399 std::string kernel_supplied_name;
400
401 if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
402 &kernel_supplied_name)) {
403 logger_->LogError("Invalid parameter: --user=%s", crash_attributes.c_str());
404 return false;
405 }
406
381 std::string exec; 407 std::string exec;
382 if (force_exec) { 408 if (force_exec) {
383 exec.assign(force_exec); 409 exec.assign(force_exec);
384 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { 410 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
385 // If for some reason we don't have the base name, avoid completely 411 // If we cannot find the exec name, use the kernel supplied name.
386 // failing by indicating an unknown name. 412 // We don't always use the kernel's since it truncates the name to
387 exec = "unknown"; 413 // 16 characters.
414 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
388 } 415 }
389 416
390 // Allow us to test the crash reporting mechanism successfully even if 417 // Allow us to test the crash reporting mechanism successfully even if
391 // other parts of the system crash. 418 // other parts of the system crash.
392 if (!FLAGS_filter_in.empty() && 419 if (!FLAGS_filter_in.empty() &&
393 (FLAGS_filter_in == "none" || 420 (FLAGS_filter_in == "none" ||
394 FLAGS_filter_in != exec)) { 421 FLAGS_filter_in != exec)) {
395 // We use a different format message to make it more obvious in tests 422 // We use a different format message to make it more obvious in tests
396 // which crashes are test generated and which are real. 423 // which crashes are test generated and which are real.
397 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s", 424 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s.",
398 exec.c_str(), pid, FLAGS_filter_in.c_str()); 425 exec.c_str(), pid, FLAGS_filter_in.c_str());
399 return true; 426 return true;
400 } 427 }
401 428
402 bool feedback = is_feedback_allowed_function_(); 429 bool feedback = is_feedback_allowed_function_();
403 const char *handling_string = "handling"; 430 const char *handling_string = "handling";
404 if (!feedback) { 431 if (!feedback) {
405 handling_string = "ignoring - no consent"; 432 handling_string = "ignoring - no consent";
406 } 433 }
407 434
408 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome 435 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
409 // crashes towards user crashes, so user crashes really mean non-Chrome 436 // crashes towards user crashes, so user crashes really mean non-Chrome
410 // user-space crashes. 437 // user-space crashes.
411 if (exec == "chrome") { 438 if (exec == "chrome") {
412 feedback = false; 439 feedback = false;
413 handling_string = "ignoring - chrome crash"; 440 handling_string = "ignoring - chrome crash";
414 } 441 }
415 442
416 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", 443 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
417 exec.c_str(), pid, signal, handling_string); 444 exec.c_str(), pid, signal, handling_string);
418 445
419 if (feedback) { 446 if (feedback) {
420 count_crash_function_(); 447 count_crash_function_();
421 448
422 if (generate_diagnostics_) { 449 if (generate_diagnostics_) {
423 bool out_of_capacity = false; 450 bool out_of_capacity = false;
424 if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) { 451 logger_->set_accumulating(true);
452 bool convert_and_enqueue_result =
453 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
454 logger_->set_accumulating(false);
455 if (!convert_and_enqueue_result) {
425 if (!out_of_capacity) 456 if (!out_of_capacity)
426 EnqueueCollectionErrorLog(pid, exec); 457 EnqueueCollectionErrorLog(pid, exec);
427 return false; 458 return false;
428 } 459 }
429 } 460 }
430 } 461 }
431 462
432 return true; 463 return true;
433 } 464 }
OLDNEW
« crash_collector.cc ('K') | « user_collector.h ('k') | user_collector_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698