blob: da4265542e6239d9f39fa1a8862e2c3d51a43ad6 [file] [log] [blame]
Chris Sosae4a86032010-06-16 17:08:34 -07001// 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
3// found in the LICENSE file.
4
Ken Mixter03403162010-08-18 15:23:16 -07005#include "crash-reporter/user_collector.h"
6
Ken Mixter777484c2010-07-23 16:22:44 -07007#include <grp.h> // For struct group.
Ken Mixter1b8fe012011-01-25 13:33:05 -08008#include <pcrecpp.h>
9#include <pcrecpp.h>
Ken Mixter777484c2010-07-23 16:22:44 -070010#include <pwd.h> // For struct passwd.
Ken Mixter2953c3a2010-10-18 14:42:20 -070011#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
Ken Mixter777484c2010-07-23 16:22:44 -070012
Chris Sosae4a86032010-06-16 17:08:34 -070013#include <string>
Ken Mixter2953c3a2010-10-18 14:42:20 -070014#include <vector>
Chris Sosae4a86032010-06-16 17:08:34 -070015
16#include "base/file_util.h"
17#include "base/logging.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070018#include "base/string_split.h"
Chris Sosae4a86032010-06-16 17:08:34 -070019#include "base/string_util.h"
Ken Mixtera3249322011-03-03 08:47:38 -080020#include "chromeos/process.h"
21#include "chromeos/syslog_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070022#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070023
Ken Mixterc6a58e02010-11-01 18:05:30 -070024#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter1b8fe012011-01-25 13:33:05 -080025DEFINE_bool(core2md_failure, false, "Core2md failure test");
26DEFINE_bool(directory_failure, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070027DEFINE_string(filter_in, "",
28 "Ignore all crashes but this for testing");
29#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070030
31static const char kCollectionErrorSignature[] =
32 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070033// This procfs file is used to cause kernel core file writing to
34// instead pipe the core file into a user space process. See
35// core(5) man page.
36static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080037static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
38// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
39// crashes, but finite to avoid infinitely recursing on crash handling.
40static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070041static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter7ac7a702010-08-13 15:43:34 -070042static const char kLeaveCoreFile[] = "/root/.leave_core";
Michael Krebs538ecbf2011-07-27 14:13:22 -070043static const char kCollectChromeFile[] =
44 "/mnt/stateful_partition/etc/collect_chrome_crashes";
Ken Mixter777484c2010-07-23 16:22:44 -070045
Ken Mixterc49dbd42010-12-14 17:44:11 -080046static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
47
Ken Mixter777484c2010-07-23 16:22:44 -070048const char *UserCollector::kUserId = "Uid:\t";
49const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070050
51UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070052 : generate_diagnostics_(false),
53 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080054 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070055 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070056}
57
58void UserCollector::Initialize(
59 UserCollector::CountCrashFunction count_crash_function,
60 const std::string &our_path,
61 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070062 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070063 CrashCollector::Initialize(count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080064 is_feedback_allowed_function);
Chris Sosae4a86032010-06-16 17:08:34 -070065 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070066 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070067 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070068}
69
70UserCollector::~UserCollector() {
71}
72
73std::string UserCollector::GetPattern(bool enabled) const {
74 if (enabled) {
Ken Mixter1b8fe012011-01-25 13:33:05 -080075 // Combine the three crash attributes into one parameter to try to reduce
76 // the size of the invocation line for crash_reporter since the kernel
77 // has a fixed-sized (128B) buffer that it will truncate into. Note that
78 // the kernel does not support quoted arguments in core_pattern.
79 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070080 } else {
81 return "core";
82 }
83}
84
85bool UserCollector::SetUpInternal(bool enabled) {
86 CHECK(initialized_);
Ken Mixtera3249322011-03-03 08:47:38 -080087 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
88
Ken Mixterc49dbd42010-12-14 17:44:11 -080089 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
90 kCorePipeLimit,
91 strlen(kCorePipeLimit)) !=
92 static_cast<int>(strlen(kCorePipeLimit))) {
Ken Mixtera3249322011-03-03 08:47:38 -080093 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
Ken Mixterc49dbd42010-12-14 17:44:11 -080094 return false;
95 }
Chris Sosae4a86032010-06-16 17:08:34 -070096 std::string pattern = GetPattern(enabled);
97 if (file_util::WriteFile(FilePath(core_pattern_file_),
98 pattern.c_str(),
99 pattern.length()) !=
100 static_cast<int>(pattern.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -0800101 LOG(ERROR) << "Unable to write " << core_pattern_file_;
Chris Sosae4a86032010-06-16 17:08:34 -0700102 return false;
103 }
104 return true;
105}
106
Ken Mixter777484c2010-07-23 16:22:44 -0700107FilePath UserCollector::GetProcessPath(pid_t pid) {
108 return FilePath(StringPrintf("/proc/%d", pid));
109}
110
111bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
112 FilePath *target) {
113 int max_size = 32;
114 scoped_array<char> buffer;
115 while (true) {
116 buffer.reset(new char[max_size + 1]);
117 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
118 if (size < 0) {
Ken Mixterd49d3622011-02-09 18:23:00 -0800119 int saved_errno = errno;
Ken Mixtera3249322011-03-03 08:47:38 -0800120 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
121 << saved_errno;
Ken Mixter777484c2010-07-23 16:22:44 -0700122 return false;
123 }
124 buffer[size] = 0;
125 if (size == max_size) {
126 // Avoid overflow when doubling.
127 if (max_size * 2 > max_size) {
128 max_size *= 2;
129 continue;
130 } else {
131 return false;
132 }
133 }
134 break;
135 }
136
137 *target = FilePath(buffer.get());
138 return true;
139}
140
141bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
142 std::string *base_name) {
143 FilePath target;
Ken Mixterd49d3622011-02-09 18:23:00 -0800144 FilePath process_path = GetProcessPath(pid);
145 FilePath exe_path = process_path.Append("exe");
146 if (!GetSymlinkTarget(exe_path, &target)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800147 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
148 << " DirectoryExists: "
149 << file_util::DirectoryExists(process_path);
Ken Mixterd49d3622011-02-09 18:23:00 -0800150 // Try to further diagnose exe readlink failure cause.
151 struct stat buf;
152 int stat_result = stat(exe_path.value().c_str(), &buf);
153 int saved_errno = errno;
154 if (stat_result < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800155 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
156 << " " << saved_errno;
Ken Mixterd49d3622011-02-09 18:23:00 -0800157 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800158 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
159 << buf.st_mode;
Ken Mixterd49d3622011-02-09 18:23:00 -0800160 }
Ken Mixter777484c2010-07-23 16:22:44 -0700161 return false;
Ken Mixterd49d3622011-02-09 18:23:00 -0800162 }
Ken Mixter777484c2010-07-23 16:22:44 -0700163 *base_name = target.BaseName().value();
164 return true;
165}
166
167bool UserCollector::GetIdFromStatus(const char *prefix,
168 IdKind kind,
169 const std::string &status_contents,
170 int *id) {
171 // From fs/proc/array.c:task_state(), this file contains:
172 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
173 std::vector<std::string> status_lines;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700174 base::SplitString(status_contents, '\n', &status_lines);
Ken Mixter777484c2010-07-23 16:22:44 -0700175 std::vector<std::string>::iterator line_iterator;
176 for (line_iterator = status_lines.begin();
177 line_iterator != status_lines.end();
178 ++line_iterator) {
179 if (line_iterator->find(prefix) == 0)
180 break;
181 }
182 if (line_iterator == status_lines.end()) {
183 return false;
184 }
185 std::string id_substring = line_iterator->substr(strlen(prefix),
186 std::string::npos);
187 std::vector<std::string> ids;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700188 base::SplitString(id_substring, '\t', &ids);
Ken Mixter777484c2010-07-23 16:22:44 -0700189 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
190 return false;
191 }
192 const char *number = ids[kind].c_str();
193 char *end_number = NULL;
194 *id = strtol(number, &end_number, 10);
195 if (*end_number != '\0')
196 return false;
197 return true;
198}
199
Ken Mixter207694d2010-10-28 15:42:37 -0700200void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
201 const std::string &exec) {
202 FilePath crash_path;
Ken Mixtera3249322011-03-03 08:47:38 -0800203 LOG(INFO) << "Writing conversion problems as separate crash report.";
Ken Mixter207694d2010-10-28 15:42:37 -0700204 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800205 LOG(ERROR) << "Could not even get log directory; out of space?";
Ken Mixter207694d2010-10-28 15:42:37 -0700206 return;
207 }
208 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
Ken Mixtera3249322011-03-03 08:47:38 -0800209 std::string error_log = chromeos::GetLog();
Ken Mixter1b8fe012011-01-25 13:33:05 -0800210 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
211 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature,
212 diag_log_path)) {
213 // We load the contents of diag_log into memory and append it to
214 // the error log. We cannot just append to files because we need
215 // to always create new files to prevent attack.
216 std::string diag_log_contents;
217 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
218 error_log.append(diag_log_contents);
219 file_util::Delete(diag_log_path, false);
220 }
Ken Mixter207694d2010-10-28 15:42:37 -0700221 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
222 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800223 // We must use WriteNewFile instead of file_util::WriteFile as we do
224 // not want to write with root access to a symlink that an attacker
225 // might have created.
Ken Mixter1b8fe012011-01-25 13:33:05 -0800226 WriteNewFile(log_path, error_log.data(), error_log.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700227 AddCrashMetaData("sig", kCollectionErrorSignature);
228 WriteCrashMetaData(meta_path, exec, log_path.value());
229}
230
Ken Mixter777484c2010-07-23 16:22:44 -0700231bool UserCollector::CopyOffProcFiles(pid_t pid,
232 const FilePath &container_dir) {
233 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800234 LOG(ERROR) << "Could not create " << container_dir.value().c_str();
Ken Mixter777484c2010-07-23 16:22:44 -0700235 return false;
236 }
237 FilePath process_path = GetProcessPath(pid);
238 if (!file_util::PathExists(process_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800239 LOG(ERROR) << "Path " << process_path.value() << " does not exist";
Ken Mixter777484c2010-07-23 16:22:44 -0700240 return false;
241 }
242 static const char *proc_files[] = {
243 "auxv",
244 "cmdline",
245 "environ",
246 "maps",
247 "status"
248 };
249 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
250 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
251 container_dir.Append(proc_files[i]))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800252 LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
Ken Mixter777484c2010-07-23 16:22:44 -0700253 return false;
254 }
255 }
256 return true;
257}
258
Ken Mixter777484c2010-07-23 16:22:44 -0700259bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700260 FilePath *crash_file_path,
261 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700262 FilePath process_path = GetProcessPath(pid);
263 std::string status;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800264 if (FLAGS_directory_failure) {
Ken Mixtera3249322011-03-03 08:47:38 -0800265 LOG(ERROR) << "Purposefully failing to create spool directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700266 return false;
267 }
Ken Mixter777484c2010-07-23 16:22:44 -0700268 if (!file_util::ReadFileToString(process_path.Append("status"),
269 &status)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800270 LOG(ERROR) << "Could not read status file";
271 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
272 << file_util::DirectoryExists(process_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700273 return false;
274 }
275 int process_euid;
276 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800277 LOG(ERROR) << "Could not find euid in status file";
Ken Mixter777484c2010-07-23 16:22:44 -0700278 return false;
279 }
Ken Mixter207694d2010-10-28 15:42:37 -0700280 if (!GetCreatedCrashDirectoryByEuid(process_euid,
281 crash_file_path,
282 out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800283 LOG(ERROR) << "Could not create crash directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700284 return false;
285 }
286 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700287}
288
289bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
290 // Copy off all stdin to a core file.
291 FilePath stdin_path("/dev/fd/0");
292 if (file_util::CopyFile(stdin_path, core_path)) {
293 return true;
294 }
295
Ken Mixtera3249322011-03-03 08:47:38 -0800296 LOG(ERROR) << "Could not write core file";
Ken Mixter777484c2010-07-23 16:22:44 -0700297 // If the file system was full, make sure we remove any remnants.
298 file_util::Delete(core_path, false);
299 return false;
300}
301
Ken Mixter207694d2010-10-28 15:42:37 -0700302bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
303 const FilePath &procfs_directory,
304 const FilePath &minidump_path,
305 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700306 FilePath output_path = temp_directory.Append("output");
Ken Mixtera3249322011-03-03 08:47:38 -0800307 chromeos::ProcessImpl core2md;
308 core2md.RedirectOutput(output_path.value());
309 core2md.AddArg(kCoreToMinidumpConverterPath);
310 core2md.AddArg(core_path.value());
311 core2md.AddArg(procfs_directory.value());
Ken Mixter2953c3a2010-10-18 14:42:20 -0700312
Ken Mixtera3249322011-03-03 08:47:38 -0800313 if (!FLAGS_core2md_failure) {
314 core2md.AddArg(minidump_path.value());
315 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700316 // To test how core2md errors are propagaged, cause an error
317 // by forgetting a required argument.
Ken Mixter207694d2010-10-28 15:42:37 -0700318 }
319
Ken Mixtera3249322011-03-03 08:47:38 -0800320 int errorlevel = core2md.Run();
Ken Mixter777484c2010-07-23 16:22:44 -0700321
322 std::string output;
323 file_util::ReadFileToString(output_path, &output);
324 if (errorlevel != 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800325 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
326 << " [result=" << errorlevel << "]: " << output;
Ken Mixter777484c2010-07-23 16:22:44 -0700327 return false;
328 }
329
330 if (!file_util::PathExists(minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800331 LOG(ERROR) << "Minidump file " << minidump_path.value()
332 << " was not created";
Ken Mixter777484c2010-07-23 16:22:44 -0700333 return false;
334 }
335 return true;
336}
337
Ken Mixter207694d2010-10-28 15:42:37 -0700338bool UserCollector::ConvertCoreToMinidump(pid_t pid,
339 const FilePath &container_dir,
340 const FilePath &core_path,
341 const FilePath &minidump_path) {
Ken Mixter777484c2010-07-23 16:22:44 -0700342 if (!CopyOffProcFiles(pid, container_dir)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700343 return false;
344 }
345
Ken Mixter777484c2010-07-23 16:22:44 -0700346 if (!CopyStdinToCoreFile(core_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700347 return false;
348 }
349
Ken Mixter207694d2010-10-28 15:42:37 -0700350 bool conversion_result = RunCoreToMinidump(
351 core_path,
352 container_dir, // procfs directory
353 minidump_path,
354 container_dir); // temporary directory
Ken Mixteree849c52010-09-30 15:30:10 -0700355
Ken Mixter777484c2010-07-23 16:22:44 -0700356 if (conversion_result) {
Ken Mixtera3249322011-03-03 08:47:38 -0800357 LOG(INFO) << "Stored minidump to " << minidump_path.value();
Ken Mixter777484c2010-07-23 16:22:44 -0700358 }
359
Ken Mixter207694d2010-10-28 15:42:37 -0700360 return conversion_result;
361}
362
363bool UserCollector::ConvertAndEnqueueCrash(int pid,
364 const std::string &exec,
365 bool *out_of_capacity) {
366 FilePath crash_path;
367 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800368 LOG(ERROR) << "Unable to find/create process-specific crash path";
Ken Mixter207694d2010-10-28 15:42:37 -0700369 return false;
370 }
371
372 // Directory like /tmp/crash_reporter.1234 which contains the
373 // procfs entries and other temporary files used during conversion.
374 FilePath container_dir = FilePath("/tmp").Append(
375 StringPrintf("crash_reporter.%d", pid));
Ken Mixter1b8fe012011-01-25 13:33:05 -0800376 // Delete a pre-existing directory from crash reporter that may have
377 // been left around for diagnostics from a failed conversion attempt.
378 // If we don't, existing files can cause forking to fail.
379 file_util::Delete(container_dir, true);
Ken Mixter207694d2010-10-28 15:42:37 -0700380 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
381 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
382 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
383 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800384 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
385
386 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path))
387 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700388
389 if (!ConvertCoreToMinidump(pid, container_dir, core_path,
390 minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800391 LOG(INFO) << "Leaving core file at " << core_path.value()
392 << " due to conversion error";
Ken Mixter207694d2010-10-28 15:42:37 -0700393 return false;
394 }
395
396 // Here we commit to sending this file. We must not return false
397 // after this point or we will generate a log report as well as a
398 // crash report.
399 WriteCrashMetaData(meta_path,
400 exec,
401 minidump_path.value());
402
Michael Krebs538ecbf2011-07-27 14:13:22 -0700403 if (!IsDeveloperImage()) {
Ken Mixter777484c2010-07-23 16:22:44 -0700404 file_util::Delete(core_path, false);
405 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800406 LOG(INFO) << "Leaving core file at " << core_path.value()
407 << " due to developer image";
Ken Mixter777484c2010-07-23 16:22:44 -0700408 }
409
Ken Mixter207694d2010-10-28 15:42:37 -0700410 file_util::Delete(container_dir, true);
411 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700412}
413
Ken Mixter1b8fe012011-01-25 13:33:05 -0800414bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
415 pid_t *pid, int *signal,
416 std::string *kernel_supplied_name) {
417 pcrecpp::RE re("(\\d+):(\\d+):(.*)");
418 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
419}
420
Michael Krebs538ecbf2011-07-27 14:13:22 -0700421bool UserCollector::IsDeveloperImage() {
422 // If we're testing crash reporter itself, we don't want to special-case
423 // for developer images.
424 if (IsCrashTestInProgress())
425 return false;
426 return file_util::PathExists(FilePath(kLeaveCoreFile));
427}
428
429bool UserCollector::ShouldIgnoreChromeCrashes() {
430 // If we're testing crash reporter itself, we don't want to allow an
431 // override for chrome crashes. And, let's be conservative and only
432 // allow an override for developer images.
433 if (!IsCrashTestInProgress() && IsDeveloperImage()) {
434 // Check if there's an override to indicate we should indeed collect
435 // chrome crashes. This allows the crashes to still be tracked when
436 // they occur in autotests. See "crosbug.com/17987".
437 if (file_util::PathExists(FilePath(kCollectChromeFile)))
438 return false;
439 }
440 // We default to ignoring chrome crashes.
441 return true;
442}
443
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700444bool UserCollector::ShouldDump(bool has_owner_consent,
445 bool is_developer,
Michael Krebs538ecbf2011-07-27 14:13:22 -0700446 bool ignore_chrome_crashes,
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700447 const std::string &exec,
448 std::string *reason) {
449 reason->clear();
450
451 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
452 // crashes towards user crashes, so user crashes really mean non-Chrome
453 // user-space crashes.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700454 if ((exec == "chrome" || exec == "supplied_chrome") &&
455 ignore_chrome_crashes) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700456 *reason = "ignoring - chrome crash";
457 return false;
458 }
459
460 // For developer builds, we always want to keep the crash reports unless
461 // we're testing the crash facilities themselves. This overrides
462 // feedback. Crash sending still obeys consent.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700463 if (is_developer) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700464 *reason = "developer build - not testing - always dumping";
465 return true;
466 }
467
468 if (!has_owner_consent) {
469 *reason = "ignoring - no consent";
470 return false;
471 }
472
473 *reason = "handling";
474 return true;
475}
476
Ken Mixter1b8fe012011-01-25 13:33:05 -0800477bool UserCollector::HandleCrash(const std::string &crash_attributes,
478 const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700479 CHECK(initialized_);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800480 int pid = 0;
481 int signal = 0;
482 std::string kernel_supplied_name;
483
484 if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
485 &kernel_supplied_name)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800486 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800487 return false;
488 }
489
Ken Mixter777484c2010-07-23 16:22:44 -0700490 std::string exec;
491 if (force_exec) {
492 exec.assign(force_exec);
493 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
Ken Mixter1b8fe012011-01-25 13:33:05 -0800494 // If we cannot find the exec name, use the kernel supplied name.
495 // We don't always use the kernel's since it truncates the name to
496 // 16 characters.
497 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700498 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700499
500 // Allow us to test the crash reporting mechanism successfully even if
501 // other parts of the system crash.
502 if (!FLAGS_filter_in.empty() &&
503 (FLAGS_filter_in == "none" ||
504 FLAGS_filter_in != exec)) {
505 // We use a different format message to make it more obvious in tests
506 // which crashes are test generated and which are real.
Ken Mixtera3249322011-03-03 08:47:38 -0800507 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
508 << "filter_in=" << FLAGS_filter_in << ".";
Ken Mixterc6a58e02010-11-01 18:05:30 -0700509 return true;
510 }
511
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700512 std::string reason;
513 bool dump = ShouldDump(is_feedback_allowed_function_(),
Michael Krebs538ecbf2011-07-27 14:13:22 -0700514 IsDeveloperImage(),
515 ShouldIgnoreChromeCrashes(),
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700516 exec,
517 &reason);
Ken Mixter2105b492010-11-09 16:14:38 -0800518
Ken Mixtera3249322011-03-03 08:47:38 -0800519 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700520 << "] sig " << signal << " (" << reason << ")";
Chris Sosae4a86032010-06-16 17:08:34 -0700521
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700522 if (dump) {
Chris Sosae4a86032010-06-16 17:08:34 -0700523 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700524
Ken Mixter03403162010-08-18 15:23:16 -0700525 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700526 bool out_of_capacity = false;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800527 bool convert_and_enqueue_result =
528 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800529 if (!convert_and_enqueue_result) {
Ken Mixter207694d2010-10-28 15:42:37 -0700530 if (!out_of_capacity)
531 EnqueueCollectionErrorLog(pid, exec);
532 return false;
533 }
Ken Mixter03403162010-08-18 15:23:16 -0700534 }
Ken Mixter777484c2010-07-23 16:22:44 -0700535 }
Ken Mixter207694d2010-10-28 15:42:37 -0700536
Ken Mixter777484c2010-07-23 16:22:44 -0700537 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700538}