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

Side by Side Diff: user_collector.cc

Issue 3040013: Start invoking core2md to implement full system crash reporting (Closed) Base URL: ssh://git@chromiumos-git//crash-reporter.git
Patch Set: Replace all STREQ macros Created 10 years, 5 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
« no previous file with comments | « user_collector.h ('k') | user_collector_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <grp.h> // For struct group.
6 #include <pwd.h> // For struct passwd.
7 #include <sys/types.h> // For getpwuid_r and getgrnam_r.
8
5 #include <string> 9 #include <string>
6 10
7 #include "base/file_util.h" 11 #include "base/file_util.h"
8 #include "base/logging.h" 12 #include "base/logging.h"
9 #include "base/string_util.h" 13 #include "base/string_util.h"
10 #include "crash-reporter/user_collector.h" 14 #include "crash-reporter/user_collector.h"
11 #include "metrics/metrics_library.h" 15 #include "metrics/metrics_library.h"
12 16
13 // This procfs file is used to cause kernel core file writing to 17 // This procfs file is used to cause kernel core file writing to
14 // instead pipe the core file into a user space process. See 18 // instead pipe the core file into a user space process. See
15 // core(5) man page. 19 // core(5) man page.
16 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; 20 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
21 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
22 static const char kDefaultUserName[] = "chronos";
23 static const char kLeaveCoreFile[] = "/etc/leave_core";
24 static const char kSystemCrashPath[] = "/var/spool/crash";
25 static const char kUserCrashPath[] = "/home/chronos/user/crash";
26
27 // Directory mode of the user crash spool directory.
28 static const mode_t kUserCrashPathMode = 0755;
29
30 // Directory mode of the system crash spool directory.
31 static const mode_t kSystemCrashPathMode = 01755;
32
33 static const uid_t kRootOwner = 0;
34 static const uid_t kRootGroup = 0;
35
36 const char *UserCollector::kUserId = "Uid:\t";
37 const char *UserCollector::kGroupId = "Gid:\t";
17 38
18 UserCollector::UserCollector() 39 UserCollector::UserCollector()
19 : core_pattern_file_(kCorePatternFile), 40 : generate_diagnostics_(false),
41 core_pattern_file_(kCorePatternFile),
20 count_crash_function_(NULL), 42 count_crash_function_(NULL),
21 initialized_(false), 43 initialized_(false),
22 is_feedback_allowed_function_(NULL), 44 is_feedback_allowed_function_(NULL),
23 logger_(NULL) { 45 logger_(NULL) {
24 } 46 }
25 47
26 void UserCollector::Initialize( 48 void UserCollector::Initialize(
27 UserCollector::CountCrashFunction count_crash_function, 49 UserCollector::CountCrashFunction count_crash_function,
28 const std::string &our_path, 50 const std::string &our_path,
29 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, 51 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
30 SystemLogging *logger) { 52 SystemLogging *logger,
53 bool generate_diagnostics) {
31 CHECK(count_crash_function != NULL); 54 CHECK(count_crash_function != NULL);
32 CHECK(is_feedback_allowed_function != NULL); 55 CHECK(is_feedback_allowed_function != NULL);
33 CHECK(logger != NULL); 56 CHECK(logger != NULL);
34 57
35 count_crash_function_ = count_crash_function; 58 count_crash_function_ = count_crash_function;
36 our_path_ = our_path; 59 our_path_ = our_path;
37 is_feedback_allowed_function_ = is_feedback_allowed_function; 60 is_feedback_allowed_function_ = is_feedback_allowed_function;
38 logger_ = logger; 61 logger_ = logger;
39 initialized_ = true; 62 initialized_ = true;
63 generate_diagnostics_ = generate_diagnostics;
40 } 64 }
41 65
42 UserCollector::~UserCollector() { 66 UserCollector::~UserCollector() {
43 } 67 }
44 68
45 std::string UserCollector::GetPattern(bool enabled) const { 69 std::string UserCollector::GetPattern(bool enabled) const {
46 if (enabled) { 70 if (enabled) {
47 return StringPrintf("|%s --signal=%%s --pid=%%p --exec=%%e", 71 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
48 our_path_.c_str());
49 } else { 72 } else {
50 return "core"; 73 return "core";
51 } 74 }
52 } 75 }
53 76
54 bool UserCollector::SetUpInternal(bool enabled) { 77 bool UserCollector::SetUpInternal(bool enabled) {
55 CHECK(initialized_); 78 CHECK(initialized_);
56 logger_->LogInfo("%s crash handling", enabled ? "Enabling" : "Disabling"); 79 logger_->LogInfo("%s crash handling", enabled ? "Enabling" : "Disabling");
57 std::string pattern = GetPattern(enabled); 80 std::string pattern = GetPattern(enabled);
58 if (file_util::WriteFile(FilePath(core_pattern_file_), 81 if (file_util::WriteFile(FilePath(core_pattern_file_),
59 pattern.c_str(), 82 pattern.c_str(),
60 pattern.length()) != 83 pattern.length()) !=
61 static_cast<int>(pattern.length())) { 84 static_cast<int>(pattern.length())) {
62 logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); 85 logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
63 return false; 86 return false;
64 } 87 }
65 return true; 88 return true;
66 } 89 }
67 90
68 void UserCollector::HandleCrash(int signal, int pid, const std::string &exec) { 91 FilePath UserCollector::GetProcessPath(pid_t pid) {
92 return FilePath(StringPrintf("/proc/%d", pid));
93 }
94
95 bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
96 FilePath *target) {
97 int max_size = 32;
98 scoped_array<char> buffer;
99 while (true) {
100 buffer.reset(new char[max_size + 1]);
101 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
102 if (size < 0) {
103 return false;
104 }
105 buffer[size] = 0;
106 if (size == max_size) {
107 // Avoid overflow when doubling.
108 if (max_size * 2 > max_size) {
109 max_size *= 2;
110 continue;
111 } else {
112 return false;
113 }
114 }
115 break;
116 }
117
118 *target = FilePath(buffer.get());
119 return true;
120 }
121
122 bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
123 std::string *base_name) {
124 FilePath target;
125 if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
126 return false;
127 *base_name = target.BaseName().value();
128 return true;
129 }
130
131 bool UserCollector::GetIdFromStatus(const char *prefix,
132 IdKind kind,
133 const std::string &status_contents,
134 int *id) {
135 // From fs/proc/array.c:task_state(), this file contains:
136 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
137 std::vector<std::string> status_lines;
138 SplitString(status_contents, '\n', &status_lines);
139 std::vector<std::string>::iterator line_iterator;
140 for (line_iterator = status_lines.begin();
141 line_iterator != status_lines.end();
142 ++line_iterator) {
143 if (line_iterator->find(prefix) == 0)
144 break;
145 }
146 if (line_iterator == status_lines.end()) {
147 return false;
148 }
149 std::string id_substring = line_iterator->substr(strlen(prefix),
150 std::string::npos);
151 std::vector<std::string> ids;
152 SplitString(id_substring, '\t', &ids);
153 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
154 return false;
155 }
156 const char *number = ids[kind].c_str();
157 char *end_number = NULL;
158 *id = strtol(number, &end_number, 10);
159 if (*end_number != '\0')
160 return false;
161 return true;
162 }
163
164 bool UserCollector::GetUserInfoFromName(const std::string &name,
165 uid_t *uid,
166 gid_t *gid) {
167 char storage[256];
168 struct passwd passwd_storage;
169 struct passwd *passwd_result = NULL;
170
171 if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
172 &passwd_result) != 0 || passwd_result == NULL) {
173 logger_->LogError("Cannot find user named %s", name.c_str());
174 return false;
175 }
176
177 *uid = passwd_result->pw_uid;
178 *gid = passwd_result->pw_gid;
179 return true;
180 }
181
182 bool UserCollector::CopyOffProcFiles(pid_t pid,
183 const FilePath &container_dir) {
184 if (!file_util::CreateDirectory(container_dir)) {
185 logger_->LogInfo("Could not create %s", container_dir.value().c_str());
186 return false;
187 }
188 FilePath process_path = GetProcessPath(pid);
189 if (!file_util::PathExists(process_path)) {
190 logger_->LogWarning("Path %s does not exist",
191 process_path.value().c_str());
192 return false;
193 }
194 static const char *proc_files[] = {
195 "auxv",
196 "cmdline",
197 "environ",
198 "maps",
199 "status"
200 };
201 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
202 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
203 container_dir.Append(proc_files[i]))) {
204 logger_->LogWarning("Could not copy %s file", proc_files[i]);
205 return false;
206 }
207 }
208 return true;
209 }
210
211 FilePath UserCollector::GetCrashDirectoryInfo(
212 uid_t process_euid,
213 uid_t default_user_id,
214 gid_t default_user_group,
215 mode_t *mode,
216 uid_t *directory_owner,
217 gid_t *directory_group) {
218 if (process_euid == default_user_id) {
219 *mode = kUserCrashPathMode;
220 *directory_owner = default_user_id;
221 *directory_group = default_user_group;
222 return FilePath(kUserCrashPath);
223 } else {
224 *mode = kSystemCrashPathMode;
225 *directory_owner = kRootOwner;
226 *directory_group = kRootGroup;
227 return FilePath(kSystemCrashPath);
228 }
229 }
230
231 bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
232 FilePath *crash_file_path) {
233 FilePath process_path = GetProcessPath(pid);
234 std::string status;
235 if (!file_util::ReadFileToString(process_path.Append("status"),
236 &status)) {
237 logger_->LogError("Could not read status file");
238 return false;
239 }
240 int process_euid;
241 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
242 logger_->LogError("Could not find euid in status file");
243 return false;
244 }
245 uid_t default_user_id;
246 gid_t default_user_group;
247 if (!GetUserInfoFromName(kDefaultUserName,
248 &default_user_id,
249 &default_user_group)) {
250 logger_->LogError("Could not find default user info");
251 return false;
252 }
253 mode_t directory_mode;
254 uid_t directory_owner;
255 gid_t directory_group;
256 *crash_file_path =
257 GetCrashDirectoryInfo(process_euid,
258 default_user_id,
259 default_user_group,
260 &directory_mode,
261 &directory_owner,
262 &directory_group);
263
264
265 if (!file_util::PathExists(*crash_file_path)) {
266 // Create the spool directory with the appropriate mode (regardless of
267 // umask) and ownership.
268 mode_t old_mask = umask(0);
269 if (mkdir(crash_file_path->value().c_str(), directory_mode) < 0 ||
270 chown(crash_file_path->value().c_str(),
271 directory_owner,
272 directory_group) < 0) {
273 logger_->LogError("Unable to create appropriate crash directory");
274 return false;
275 }
276 umask(old_mask);
277 }
278
279 if (!file_util::PathExists(*crash_file_path)) {
280 logger_->LogError("Unable to create crash directory %s",
281 crash_file_path->value().c_str());
282 return false;
283 }
284
285
286 return true;
287 }
288
289 std::string UserCollector::FormatDumpBasename(const std::string &exec_name,
290 time_t timestamp,
291 pid_t pid) {
292 struct tm tm;
293 localtime_r(&timestamp, &tm);
294 return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
295 exec_name.c_str(),
296 tm.tm_year + 1900,
297 tm.tm_mon + 1,
298 tm.tm_mday,
299 tm.tm_hour,
300 tm.tm_min,
301 tm.tm_sec,
302 pid);
303 }
304
305 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
306 // Copy off all stdin to a core file.
307 FilePath stdin_path("/dev/fd/0");
308 if (file_util::CopyFile(stdin_path, core_path)) {
309 return true;
310 }
311
312 logger_->LogError("Could not write core file");
313 // If the file system was full, make sure we remove any remnants.
314 file_util::Delete(core_path, false);
315 return false;
316 }
317
318 bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path,
319 const FilePath &procfs_directory,
320 const FilePath &minidump_path,
321 const FilePath &temp_directory) {
322 // TODO(kmixter): Rewrite to use process_util once it's included in
323 // libchrome.
324 FilePath output_path = temp_directory.Append("output");
325 std::string core2md_command =
326 StringPrintf("\"%s\" \"%s\" \"%s\" \"%s\" > \"%s\" 2>&1",
327 kCoreToMinidumpConverterPath,
328 core_path.value().c_str(),
329 procfs_directory.value().c_str(),
330 minidump_path.value().c_str(),
331 output_path.value().c_str());
332 int errorlevel = system(core2md_command.c_str());
333
334 std::string output;
335 file_util::ReadFileToString(output_path, &output);
336 if (errorlevel != 0) {
337 logger_->LogInfo("Problem during %s [result=%d]: %s",
338 core2md_command.c_str(),
339 errorlevel,
340 output.c_str());
341 return false;
342 }
343
344 if (!file_util::PathExists(minidump_path)) {
345 logger_->LogError("Minidump file %s was not created",
346 minidump_path.value().c_str());
347 return false;
348 }
349 return true;
350 }
351
352 bool UserCollector::GenerateDiagnostics(pid_t pid,
353 const std::string &exec_name) {
354 FilePath container_dir("/tmp");
355 container_dir = container_dir.Append(
356 StringPrintf("crash_reporter.%d", pid));
357
358 if (!CopyOffProcFiles(pid, container_dir)) {
359 file_util::Delete(container_dir, true);
360 return false;
361 }
362
363 FilePath spool_path;
364 if (!GetCreatedCrashDirectory(pid, &spool_path)) {
365 file_util::Delete(container_dir, true);
366 return false;
367 }
368 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid);
369 FilePath core_path = spool_path.Append(
370 StringPrintf("%s.core", dump_basename.c_str()));
371
372 if (!CopyStdinToCoreFile(core_path)) {
373 file_util::Delete(container_dir, true);
374 return false;
375 }
376
377 FilePath minidump_path = spool_path.Append(
378 StringPrintf("%s.dmp", dump_basename.c_str()));
379
380 bool conversion_result = true;
381 if (!ConvertCoreToMinidump(core_path,
382 container_dir, // procfs directory
383 minidump_path,
384 container_dir)) { // temporary directory
385 // Note we leave the container directory for inspection.
386 conversion_result = false;
387 }
388
389 if (conversion_result) {
390 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
391 }
392
393 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
394 file_util::Delete(core_path, false);
395 } else {
396 logger_->LogInfo("Leaving core file at %s", core_path.value().c_str());
397 }
398
399 return conversion_result;
400 }
401
402 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
69 CHECK(initialized_); 403 CHECK(initialized_);
404 std::string exec;
405 if (force_exec) {
406 exec.assign(force_exec);
407 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
408 // If for some reason we don't have the base name, avoid completely
409 // failing by indicating an unknown name.
410 exec = "unknown";
411 }
70 logger_->LogWarning("Received crash notification for %s[%d] sig %d", 412 logger_->LogWarning("Received crash notification for %s[%d] sig %d",
71 exec.c_str(), pid, signal); 413 exec.c_str(), pid, signal);
72 414
73 if (is_feedback_allowed_function_()) { 415 if (is_feedback_allowed_function_()) {
74 count_crash_function_(); 416 count_crash_function_();
75 } 417 }
76 } 418
419 if (generate_diagnostics_) {
420 return GenerateDiagnostics(pid, exec);
421 }
422 return true;
423 }
OLDNEW
« no previous file with comments | « user_collector.h ('k') | user_collector_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698