blob: 540616043a0cb99421906000a64e72266dee4f2f [file] [log] [blame]
Simon Quef70060c2012-04-09 19:07:07 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Ken Mixter03403162010-08-18 15:23:16 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Steve Fung129bea52015-07-23 13:11:15 -07005#include "crash_collector.h"
Ken Mixter03403162010-08-18 15:23:16 -07006
Ken Mixter04ec10f2010-08-26 16:02:02 -07007#include <dirent.h>
Ken Mixter9b346472010-11-07 13:45:45 -08008#include <fcntl.h> // For file creation modes.
Alex Vakulenko7589aff2014-07-30 10:07:35 -07009#include <inttypes.h>
Ben Chanf3811f52014-08-26 06:46:38 -070010#include <linux/limits.h> // PATH_MAX
Ken Mixter03403162010-08-18 15:23:16 -070011#include <pwd.h> // For struct passwd.
12#include <sys/types.h> // for mode_t.
Ken Mixter9b346472010-11-07 13:45:45 -080013#include <sys/wait.h> // For waitpid.
14#include <unistd.h> // For execv and fork.
Ken Mixter03403162010-08-18 15:23:16 -070015
Ken Mixteree849c52010-09-30 15:30:10 -070016#include <set>
Ben Chan7e776902014-06-18 13:19:51 -070017#include <utility>
Simon Quef70060c2012-04-09 19:07:07 -070018#include <vector>
Ken Mixteree849c52010-09-30 15:30:10 -070019
Ben Chanab6cc902014-09-05 08:21:06 -070020#include <base/files/file_util.h>
Ben Chan7e776902014-06-18 13:19:51 -070021#include <base/logging.h>
22#include <base/posix/eintr_wrapper.h>
23#include <base/strings/string_split.h>
24#include <base/strings/string_util.h>
25#include <base/strings/stringprintf.h>
Daniel Erat731da332015-01-28 09:48:10 -070026#include <chromeos/key_value_store.h>
Ben Chan7e776902014-06-18 13:19:51 -070027#include <chromeos/process.h>
Ken Mixter03403162010-08-18 15:23:16 -070028
Daniel Erat731da332015-01-28 09:48:10 -070029namespace {
30
31const char kCollectChromeFile[] =
Michael Krebs4fe30db2011-08-05 13:54:52 -070032 "/mnt/stateful_partition/etc/collect_chrome_crashes";
Daniel Erat731da332015-01-28 09:48:10 -070033const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress";
34const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
35const char kDefaultUserName[] = "chronos";
36const char kLeaveCoreFile[] = "/root/.leave_core";
37const char kLsbRelease[] = "/etc/lsb-release";
38const char kShellPath[] = "/bin/sh";
Steve Fungab2ac7d2015-08-14 17:58:05 -070039const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
Daniel Erat731da332015-01-28 09:48:10 -070040const char kUploadVarPrefix[] = "upload_var_";
41const char kUploadFilePrefix[] = "upload_file_";
42
43// Key of the lsb-release entry containing the OS version.
44const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION";
45
Mike Frysingerf19b5182013-05-17 19:36:47 -040046// Normally this path is not used. Unfortunately, there are a few edge cases
47// where we need this. Any process that runs as kDefaultUserName that crashes
48// is consider a "user crash". That includes the initial Chrome browser that
49// runs the login screen. If that blows up, there is no logged in user yet,
50// so there is no per-user dir for us to stash things in. Instead we fallback
51// to this path as it is at least encrypted on a per-system basis.
52//
53// This also comes up when running autotests. The GUI is sitting at the login
54// screen while tests are sshing in, changing users, and triggering crashes as
55// the user (purposefully).
Daniel Erat731da332015-01-28 09:48:10 -070056const char kFallbackUserCrashPath[] = "/home/chronos/crash";
Ken Mixter03403162010-08-18 15:23:16 -070057
58// Directory mode of the user crash spool directory.
Daniel Erat731da332015-01-28 09:48:10 -070059const mode_t kUserCrashPathMode = 0755;
Ken Mixter03403162010-08-18 15:23:16 -070060
61// Directory mode of the system crash spool directory.
Daniel Erat731da332015-01-28 09:48:10 -070062const mode_t kSystemCrashPathMode = 01755;
Ken Mixter03403162010-08-18 15:23:16 -070063
Daniel Erat731da332015-01-28 09:48:10 -070064const uid_t kRootOwner = 0;
65const uid_t kRootGroup = 0;
66
67} // namespace
Ken Mixter03403162010-08-18 15:23:16 -070068
Ken Mixterda5db7a2010-09-17 13:50:42 -070069// Maximum crash reports per crash spool directory. Note that this is
70// a separate maximum from the maximum rate at which we upload these
71// diagnostics. The higher this rate is, the more space we allow for
72// core files, minidumps, and kcrash logs, and equivalently the more
73// processor and I/O bandwidth we dedicate to handling these crashes when
74// many occur at once. Also note that if core files are configured to
75// be left on the file system, we stop adding crashes when either the
76// number of core files or minidumps reaches this number.
77const int CrashCollector::kMaxCrashDirectorySize = 32;
Ken Mixter04ec10f2010-08-26 16:02:02 -070078
Simon Que9f90aca2013-02-19 17:19:52 -080079using base::FilePath;
Mike Frysingera557c112014-02-05 22:55:39 -050080using base::StringPrintf;
Simon Que9f90aca2013-02-19 17:19:52 -080081
Ken Mixterafcf8082010-10-26 14:45:01 -070082CrashCollector::CrashCollector()
Lei Zhang9b1f3002014-04-24 02:10:57 -070083 : lsb_release_(kLsbRelease),
Simon Queacc79382012-05-04 18:10:09 -070084 log_config_path_(kDefaultLogConfig) {
Ken Mixter03403162010-08-18 15:23:16 -070085}
86
87CrashCollector::~CrashCollector() {
88}
89
90void CrashCollector::Initialize(
91 CrashCollector::CountCrashFunction count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080092 CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
Ben Chan262d7982014-09-18 08:05:20 -070093 CHECK(count_crash_function);
94 CHECK(is_feedback_allowed_function);
Ken Mixter03403162010-08-18 15:23:16 -070095
96 count_crash_function_ = count_crash_function;
97 is_feedback_allowed_function_ = is_feedback_allowed_function;
Ken Mixter03403162010-08-18 15:23:16 -070098}
99
Ken Mixter9b346472010-11-07 13:45:45 -0800100int CrashCollector::WriteNewFile(const FilePath &filename,
101 const char *data,
102 int size) {
103 int fd = HANDLE_EINTR(open(filename.value().c_str(),
104 O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
105 if (fd < 0) {
106 return -1;
107 }
108
Alex Vakulenko859ee452014-12-10 12:52:31 -0800109 int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
Mike Frysingerf1a50142014-05-14 16:05:09 -0400110 IGNORE_EINTR(close(fd));
Ken Mixter9b346472010-11-07 13:45:45 -0800111 return rv;
112}
113
Ken Mixteree849c52010-09-30 15:30:10 -0700114std::string CrashCollector::Sanitize(const std::string &name) {
Thiemo Nagel98950962014-05-13 19:48:32 +0200115 // Make sure the sanitized name does not include any periods.
116 // The logic in crash_sender relies on this.
Ken Mixteree849c52010-09-30 15:30:10 -0700117 std::string result = name;
118 for (size_t i = 0; i < name.size(); ++i) {
119 if (!isalnum(result[i]) && result[i] != '_')
120 result[i] = '_';
121 }
122 return result;
123}
124
Ken Mixter03403162010-08-18 15:23:16 -0700125std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
126 time_t timestamp,
127 pid_t pid) {
128 struct tm tm;
129 localtime_r(&timestamp, &tm);
Ken Mixteree849c52010-09-30 15:30:10 -0700130 std::string sanitized_exec_name = Sanitize(exec_name);
Ken Mixter03403162010-08-18 15:23:16 -0700131 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
Ken Mixteree849c52010-09-30 15:30:10 -0700132 sanitized_exec_name.c_str(),
Ken Mixter03403162010-08-18 15:23:16 -0700133 tm.tm_year + 1900,
134 tm.tm_mon + 1,
135 tm.tm_mday,
136 tm.tm_hour,
137 tm.tm_min,
138 tm.tm_sec,
139 pid);
140}
141
Ken Mixter207694d2010-10-28 15:42:37 -0700142FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
143 const std::string &basename,
144 const std::string &extension) {
145 return crash_directory.Append(StringPrintf("%s.%s",
146 basename.c_str(),
147 extension.c_str()));
148}
149
Ken Mixter03403162010-08-18 15:23:16 -0700150FilePath CrashCollector::GetCrashDirectoryInfo(
Ken Mixter03403162010-08-18 15:23:16 -0700151 mode_t *mode,
152 uid_t *directory_owner,
153 gid_t *directory_group) {
Steve Fungab2ac7d2015-08-14 17:58:05 -0700154 *mode = kSystemCrashPathMode;
155 *directory_owner = kRootOwner;
156 *directory_group = kRootGroup;
157 return FilePath(kSystemCrashPath);
Ken Mixter03403162010-08-18 15:23:16 -0700158}
159
160bool CrashCollector::GetUserInfoFromName(const std::string &name,
161 uid_t *uid,
162 gid_t *gid) {
163 char storage[256];
164 struct passwd passwd_storage;
Ben Chan262d7982014-09-18 08:05:20 -0700165 struct passwd *passwd_result = nullptr;
Ken Mixter03403162010-08-18 15:23:16 -0700166
167 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
Ben Chan262d7982014-09-18 08:05:20 -0700168 &passwd_result) != 0 || passwd_result == nullptr) {
Ken Mixtera3249322011-03-03 08:47:38 -0800169 LOG(ERROR) << "Cannot find user named " << name;
Ken Mixter03403162010-08-18 15:23:16 -0700170 return false;
171 }
172
173 *uid = passwd_result->pw_uid;
174 *gid = passwd_result->pw_gid;
175 return true;
176}
177
178bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
Ken Mixter207694d2010-10-28 15:42:37 -0700179 FilePath *crash_directory,
180 bool *out_of_capacity) {
Ben Chan262d7982014-09-18 08:05:20 -0700181 if (out_of_capacity) *out_of_capacity = false;
Ken Mixter207694d2010-10-28 15:42:37 -0700182
Ken Mixter03403162010-08-18 15:23:16 -0700183 // For testing.
Lei Zhang9b1f3002014-04-24 02:10:57 -0700184 if (!forced_crash_directory_.empty()) {
185 *crash_directory = forced_crash_directory_;
Ken Mixter03403162010-08-18 15:23:16 -0700186 return true;
187 }
188
Ken Mixter03403162010-08-18 15:23:16 -0700189 mode_t directory_mode;
190 uid_t directory_owner;
191 gid_t directory_group;
192 *crash_directory =
Steve Fungab2ac7d2015-08-14 17:58:05 -0700193 GetCrashDirectoryInfo(&directory_mode,
Ken Mixter03403162010-08-18 15:23:16 -0700194 &directory_owner,
195 &directory_group);
196
Mike Frysingera557c112014-02-05 22:55:39 -0500197 if (!base::PathExists(*crash_directory)) {
Ken Mixter03403162010-08-18 15:23:16 -0700198 // Create the spool directory with the appropriate mode (regardless of
199 // umask) and ownership.
200 mode_t old_mask = umask(0);
201 if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
202 chown(crash_directory->value().c_str(),
203 directory_owner,
204 directory_group) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800205 LOG(ERROR) << "Unable to create appropriate crash directory";
Ken Mixter03403162010-08-18 15:23:16 -0700206 return false;
207 }
208 umask(old_mask);
209 }
210
Mike Frysingera557c112014-02-05 22:55:39 -0500211 if (!base::PathExists(*crash_directory)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800212 LOG(ERROR) << "Unable to create crash directory "
213 << crash_directory->value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700214 return false;
215 }
216
Ken Mixter04ec10f2010-08-26 16:02:02 -0700217 if (!CheckHasCapacity(*crash_directory)) {
Ben Chan262d7982014-09-18 08:05:20 -0700218 if (out_of_capacity) *out_of_capacity = true;
Steve Fungab2ac7d2015-08-14 17:58:05 -0700219 LOG(ERROR) << "Directory " << crash_directory->value()
220 << " is out of capacity.";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700221 return false;
222 }
223
Ken Mixter03403162010-08-18 15:23:16 -0700224 return true;
225}
Ken Mixter04ec10f2010-08-26 16:02:02 -0700226
Albert Chaulk426fcc02013-05-02 15:38:31 -0700227FilePath CrashCollector::GetProcessPath(pid_t pid) {
228 return FilePath(StringPrintf("/proc/%d", pid));
229}
230
231bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
Ben Chan8563d202014-01-27 19:30:13 -0800232 FilePath *target) {
Ben Chanf3811f52014-08-26 06:46:38 -0700233 ssize_t max_size = 64;
234 std::vector<char> buffer;
235
Albert Chaulk426fcc02013-05-02 15:38:31 -0700236 while (true) {
Ben Chanf3811f52014-08-26 06:46:38 -0700237 buffer.resize(max_size + 1);
238 ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700239 if (size < 0) {
240 int saved_errno = errno;
241 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
242 << saved_errno;
243 return false;
244 }
Ben Chanf3811f52014-08-26 06:46:38 -0700245
Albert Chaulk426fcc02013-05-02 15:38:31 -0700246 buffer[size] = 0;
247 if (size == max_size) {
Ben Chanf3811f52014-08-26 06:46:38 -0700248 max_size *= 2;
249 if (max_size > PATH_MAX) {
Albert Chaulk426fcc02013-05-02 15:38:31 -0700250 return false;
251 }
Ben Chanf3811f52014-08-26 06:46:38 -0700252 continue;
Albert Chaulk426fcc02013-05-02 15:38:31 -0700253 }
254 break;
255 }
256
Ben Chanf3811f52014-08-26 06:46:38 -0700257 *target = FilePath(buffer.data());
Albert Chaulk426fcc02013-05-02 15:38:31 -0700258 return true;
259}
260
261bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
262 std::string *base_name) {
263 FilePath target;
264 FilePath process_path = GetProcessPath(pid);
265 FilePath exe_path = process_path.Append("exe");
266 if (!GetSymlinkTarget(exe_path, &target)) {
267 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
268 << " DirectoryExists: "
Mike Frysingera557c112014-02-05 22:55:39 -0500269 << base::DirectoryExists(process_path);
Albert Chaulk426fcc02013-05-02 15:38:31 -0700270 // Try to further diagnose exe readlink failure cause.
271 struct stat buf;
272 int stat_result = stat(exe_path.value().c_str(), &buf);
273 int saved_errno = errno;
274 if (stat_result < 0) {
275 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
276 << " " << saved_errno;
277 } else {
278 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
279 << buf.st_mode;
280 }
281 return false;
282 }
283 *base_name = target.BaseName().value();
284 return true;
285}
286
Ken Mixter04ec10f2010-08-26 16:02:02 -0700287// Return true if the given crash directory has not already reached
288// maximum capacity.
289bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
290 DIR* dir = opendir(crash_directory.value().c_str());
291 if (!dir) {
Steve Fungab2ac7d2015-08-14 17:58:05 -0700292 LOG(WARNING) << "Unable to open crash directory "
293 << crash_directory.value();
Ken Mixter04ec10f2010-08-26 16:02:02 -0700294 return false;
295 }
296 struct dirent ent_buf;
297 struct dirent* ent;
Ken Mixter04ec10f2010-08-26 16:02:02 -0700298 bool full = false;
Ken Mixteree849c52010-09-30 15:30:10 -0700299 std::set<std::string> basenames;
Ben Chan262d7982014-09-18 08:05:20 -0700300 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
Ken Mixter04ec10f2010-08-26 16:02:02 -0700301 if ((strcmp(ent->d_name, ".") == 0) ||
302 (strcmp(ent->d_name, "..") == 0))
303 continue;
304
Ken Mixteree849c52010-09-30 15:30:10 -0700305 std::string filename(ent->d_name);
306 size_t last_dot = filename.rfind(".");
307 std::string basename;
308 // If there is a valid looking extension, use the base part of the
309 // name. If the only dot is the first byte (aka a dot file), treat
310 // it as unique to avoid allowing a directory full of dot files
311 // from accumulating.
312 if (last_dot != std::string::npos && last_dot != 0)
313 basename = filename.substr(0, last_dot);
314 else
315 basename = filename;
316 basenames.insert(basename);
Ken Mixter04ec10f2010-08-26 16:02:02 -0700317
Ken Mixteree849c52010-09-30 15:30:10 -0700318 if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800319 LOG(WARNING) << "Crash directory " << crash_directory.value()
320 << " already full with " << kMaxCrashDirectorySize
321 << " pending reports";
Ken Mixter04ec10f2010-08-26 16:02:02 -0700322 full = true;
323 break;
324 }
325 }
326 closedir(dir);
327 return !full;
328}
Ken Mixteree849c52010-09-30 15:30:10 -0700329
Ken Mixterc49dbd42010-12-14 17:44:11 -0800330bool CrashCollector::GetLogContents(const FilePath &config_path,
331 const std::string &exec_name,
332 const FilePath &output_file) {
Daniel Erat731da332015-01-28 09:48:10 -0700333 chromeos::KeyValueStore store;
334 if (!store.Load(config_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800335 LOG(INFO) << "Unable to read log configuration file "
336 << config_path.value();
Ken Mixterc49dbd42010-12-14 17:44:11 -0800337 return false;
338 }
339
Daniel Erat731da332015-01-28 09:48:10 -0700340 std::string command;
341 if (!store.GetString(exec_name, &command))
Ken Mixterc49dbd42010-12-14 17:44:11 -0800342 return false;
343
Ken Mixtera3249322011-03-03 08:47:38 -0800344 chromeos::ProcessImpl diag_process;
345 diag_process.AddArg(kShellPath);
Daniel Erat731da332015-01-28 09:48:10 -0700346 diag_process.AddStringOption("-c", command);
Ken Mixtera3249322011-03-03 08:47:38 -0800347 diag_process.RedirectOutput(output_file.value());
Ken Mixterc49dbd42010-12-14 17:44:11 -0800348
Daniel Erat731da332015-01-28 09:48:10 -0700349 const int result = diag_process.Run();
Ken Mixtera3249322011-03-03 08:47:38 -0800350 if (result != 0) {
Daniel Erat731da332015-01-28 09:48:10 -0700351 LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800352 return false;
353 }
354 return true;
355}
356
Ken Mixterafcf8082010-10-26 14:45:01 -0700357void CrashCollector::AddCrashMetaData(const std::string &key,
358 const std::string &value) {
359 extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
360}
361
Albert Chaulk33dfd472013-06-19 15:34:13 -0700362void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
363 const std::string &path) {
364 if (!path.empty())
365 AddCrashMetaData(kUploadFilePrefix + key, path);
366}
367
368void CrashCollector::AddCrashMetaUploadData(const std::string &key,
369 const std::string &value) {
370 if (!value.empty())
371 AddCrashMetaData(kUploadVarPrefix + key, value);
372}
373
Ken Mixteree849c52010-09-30 15:30:10 -0700374void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
Ken Mixterc909b692010-10-18 12:26:05 -0700375 const std::string &exec_name,
376 const std::string &payload_path) {
Daniel Erat731da332015-01-28 09:48:10 -0700377 chromeos::KeyValueStore store;
378 if (!store.Load(FilePath(lsb_release_))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800379 LOG(ERROR) << "Problem parsing " << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700380 // Even though there was some failure, take as much as we could read.
381 }
Daniel Erat731da332015-01-28 09:48:10 -0700382
Ken Mixteree849c52010-09-30 15:30:10 -0700383 std::string version("unknown");
Daniel Erat731da332015-01-28 09:48:10 -0700384 if (!store.GetString(kLsbVersionKey, &version)) {
385 LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from "
386 << lsb_release_;
Ken Mixteree849c52010-09-30 15:30:10 -0700387 }
Ben Chanf84ea212014-08-06 17:27:48 -0700388 int64_t payload_size = -1;
Mike Frysingera557c112014-02-05 22:55:39 -0500389 base::GetFileSize(FilePath(payload_path), &payload_size);
Ken Mixterafcf8082010-10-26 14:45:01 -0700390 std::string meta_data = StringPrintf("%sexec_name=%s\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700391 "ver=%s\n"
Ken Mixter207694d2010-10-28 15:42:37 -0700392 "payload=%s\n"
Alex Vakulenkoeaf060c2014-08-08 09:36:48 -0700393 "payload_size=%" PRId64 "\n"
Ken Mixteree849c52010-09-30 15:30:10 -0700394 "done=1\n",
Ken Mixterafcf8082010-10-26 14:45:01 -0700395 extra_metadata_.c_str(),
Ken Mixteree849c52010-09-30 15:30:10 -0700396 exec_name.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700397 version.c_str(),
Ken Mixter207694d2010-10-28 15:42:37 -0700398 payload_path.c_str(),
Ken Mixterc909b692010-10-18 12:26:05 -0700399 payload_size);
Ben Chanf30c6412014-05-22 23:09:01 -0700400 // We must use WriteNewFile instead of base::WriteFile as we
Ken Mixter9b346472010-11-07 13:45:45 -0800401 // do not want to write with root access to a symlink that an attacker
402 // might have created.
403 if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800404 LOG(ERROR) << "Unable to write " << meta_path.value();
Ken Mixteree849c52010-09-30 15:30:10 -0700405 }
406}
Thieu Le1652fb22011-03-03 12:14:43 -0800407
408bool CrashCollector::IsCrashTestInProgress() {
Mike Frysingera557c112014-02-05 22:55:39 -0500409 return base::PathExists(FilePath(kCrashTestInProgressPath));
Thieu Le1652fb22011-03-03 12:14:43 -0800410}
Michael Krebs4fe30db2011-08-05 13:54:52 -0700411
412bool CrashCollector::IsDeveloperImage() {
413 // If we're testing crash reporter itself, we don't want to special-case
414 // for developer images.
415 if (IsCrashTestInProgress())
416 return false;
Mike Frysingera557c112014-02-05 22:55:39 -0500417 return base::PathExists(FilePath(kLeaveCoreFile));
Michael Krebs4fe30db2011-08-05 13:54:52 -0700418}