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

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: clarify 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 <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. 9 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
10 10
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 our_path_ = our_path; 61 our_path_ = our_path;
62 initialized_ = true; 62 initialized_ = true;
63 generate_diagnostics_ = generate_diagnostics; 63 generate_diagnostics_ = generate_diagnostics;
64 } 64 }
65 65
66 UserCollector::~UserCollector() { 66 UserCollector::~UserCollector() {
67 } 67 }
68 68
69 std::string UserCollector::GetPattern(bool enabled) const { 69 std::string UserCollector::GetPattern(bool enabled) const {
70 if (enabled) { 70 if (enabled) {
71 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); 71 return StringPrintf("|%s --signal=%%s --pid=%%p --exec_name=%%e",
72 our_path_.c_str());
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);
187 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); 182 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
188 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 183 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
189 // We must use WriteNewFile instead of file_util::WriteFile as we do 184 // 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 185 // not want to write with root access to a symlink that an attacker
191 // might have created. 186 // might have created.
192 WriteNewFile(log_path, error_log_.data(), error_log_.length()); 187 WriteNewFile(log_path, error_log_.data(), error_log_.length());
193 AddCrashMetaData("sig", kCollectionErrorSignature); 188 AddCrashMetaData("sig", kCollectionErrorSignature);
194 WriteCrashMetaData(meta_path, exec, log_path.value()); 189 WriteCrashMetaData(meta_path, exec, log_path.value());
195 } 190 }
196 191
197 bool UserCollector::CopyOffProcFiles(pid_t pid, 192 bool UserCollector::CopyOffProcFiles(pid_t pid,
198 const FilePath &container_dir) { 193 const FilePath &container_dir) {
199 if (!file_util::CreateDirectory(container_dir)) { 194 if (!file_util::CreateDirectory(container_dir)) {
200 LogCollectionError(StringPrintf("Could not create %s", 195 logger_->LogError("Could not create %s",
201 container_dir.value().c_str())); 196 container_dir.value().c_str());
202 return false; 197 return false;
203 } 198 }
204 FilePath process_path = GetProcessPath(pid); 199 FilePath process_path = GetProcessPath(pid);
205 if (!file_util::PathExists(process_path)) { 200 if (!file_util::PathExists(process_path)) {
206 LogCollectionError(StringPrintf("Path %s does not exist", 201 logger_->LogError("Path %s does not exist", process_path.value().c_str());
207 process_path.value().c_str()));
208 return false; 202 return false;
209 } 203 }
210 static const char *proc_files[] = { 204 static const char *proc_files[] = {
211 "auxv", 205 "auxv",
212 "cmdline", 206 "cmdline",
213 "environ", 207 "environ",
214 "maps", 208 "maps",
215 "status" 209 "status"
216 }; 210 };
217 for (unsigned i = 0; i < arraysize(proc_files); ++i) { 211 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
218 if (!file_util::CopyFile(process_path.Append(proc_files[i]), 212 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
219 container_dir.Append(proc_files[i]))) { 213 container_dir.Append(proc_files[i]))) {
220 LogCollectionError(StringPrintf("Could not copy %s file", 214 logger_->LogError("Could not copy %s file", proc_files[i]);
221 proc_files[i]));
222 return false; 215 return false;
223 } 216 }
224 } 217 }
225 return true; 218 return true;
226 } 219 }
227 220
228 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, 221 bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
229 FilePath *crash_file_path, 222 FilePath *crash_file_path,
230 bool *out_of_capacity) { 223 bool *out_of_capacity) {
231 FilePath process_path = GetProcessPath(pid); 224 FilePath process_path = GetProcessPath(pid);
232 std::string status; 225 std::string status;
233 if (FLAGS_directory_failure_test) { 226 if (FLAGS_directory_failure_test) {
234 LogCollectionError("Purposefully failing to create spool directory"); 227 logger_->LogError("Purposefully failing to create spool directory");
235 return false; 228 return false;
236 } 229 }
237 if (!file_util::ReadFileToString(process_path.Append("status"), 230 if (!file_util::ReadFileToString(process_path.Append("status"),
238 &status)) { 231 &status)) {
239 LogCollectionError("Could not read status file"); 232 logger_->LogError("Could not read status file");
233 logger_->LogInfo("Path %s FileExists: %d",
234 process_path.value().c_str(),
235 file_util::DirectoryExists(process_path));
240 return false; 236 return false;
241 } 237 }
242 int process_euid; 238 int process_euid;
243 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { 239 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
244 LogCollectionError("Could not find euid in status file"); 240 logger_->LogError("Could not find euid in status file");
245 return false; 241 return false;
246 } 242 }
247 if (!GetCreatedCrashDirectoryByEuid(process_euid, 243 if (!GetCreatedCrashDirectoryByEuid(process_euid,
248 crash_file_path, 244 crash_file_path,
249 out_of_capacity)) { 245 out_of_capacity)) {
250 LogCollectionError("Could not create crash directory"); 246 logger_->LogError("Could not create crash directory");
251 return false; 247 return false;
252 } 248 }
253 return true; 249 return true;
254 } 250 }
255 251
256 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { 252 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
257 // Copy off all stdin to a core file. 253 // Copy off all stdin to a core file.
258 FilePath stdin_path("/dev/fd/0"); 254 FilePath stdin_path("/dev/fd/0");
259 if (file_util::CopyFile(stdin_path, core_path)) { 255 if (file_util::CopyFile(stdin_path, core_path)) {
260 return true; 256 return true;
261 } 257 }
262 258
263 LogCollectionError("Could not write core file"); 259 logger_->LogError("Could not write core file");
264 // If the file system was full, make sure we remove any remnants. 260 // If the file system was full, make sure we remove any remnants.
265 file_util::Delete(core_path, false); 261 file_util::Delete(core_path, false);
266 return false; 262 return false;
267 } 263 }
268 264
269 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, 265 bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
270 const FilePath &procfs_directory, 266 const FilePath &procfs_directory,
271 const FilePath &minidump_path, 267 const FilePath &minidump_path,
272 const FilePath &temp_directory) { 268 const FilePath &temp_directory) {
273 FilePath output_path = temp_directory.Append("output"); 269 FilePath output_path = temp_directory.Append("output");
274 std::vector<const char *> core2md_arguments; 270 std::vector<const char *> core2md_arguments;
275 core2md_arguments.push_back(kCoreToMinidumpConverterPath); 271 core2md_arguments.push_back(kCoreToMinidumpConverterPath);
276 core2md_arguments.push_back(core_path.value().c_str()); 272 core2md_arguments.push_back(core_path.value().c_str());
277 core2md_arguments.push_back(procfs_directory.value().c_str()); 273 core2md_arguments.push_back(procfs_directory.value().c_str());
278 core2md_arguments.push_back(minidump_path.value().c_str()); 274 core2md_arguments.push_back(minidump_path.value().c_str());
279 275
280 if (FLAGS_core2md_failure_test) { 276 if (FLAGS_core2md_failure_test) {
281 // To test how core2md errors are propagaged, cause an error 277 // To test how core2md errors are propagaged, cause an error
282 // by forgetting a required argument. 278 // by forgetting a required argument.
283 core2md_arguments.pop_back(); 279 core2md_arguments.pop_back();
284 } 280 }
285 281
282 std::string errors_during_fork;
petkov 2011/01/18 19:18:20 unused?
kmixter1 2011/01/25 21:28:07 Done.
286 int errorlevel = ForkExecAndPipe(core2md_arguments, 283 int errorlevel = ForkExecAndPipe(core2md_arguments,
287 output_path.value().c_str()); 284 output_path.value().c_str());
288 285
289 std::string output; 286 std::string output;
290 file_util::ReadFileToString(output_path, &output); 287 file_util::ReadFileToString(output_path, &output);
291 if (errorlevel != 0) { 288 if (errorlevel != 0) {
292 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", 289 logger_->LogError("Problem during %s [result=%d]: %s",
293 kCoreToMinidumpConverterPath, 290 kCoreToMinidumpConverterPath,
294 errorlevel, 291 errorlevel,
295 output.c_str())); 292 output.c_str());
296 return false; 293 return false;
297 } 294 }
298 295
299 if (!file_util::PathExists(minidump_path)) { 296 if (!file_util::PathExists(minidump_path)) {
300 LogCollectionError(StringPrintf("Minidump file %s was not created", 297 logger_->LogError("Minidump file %s was not created",
301 minidump_path.value().c_str())); 298 minidump_path.value().c_str());
302 return false; 299 return false;
303 } 300 }
304 return true; 301 return true;
305 } 302 }
306 303
307 bool UserCollector::ConvertCoreToMinidump(pid_t pid, 304 bool UserCollector::ConvertCoreToMinidump(pid_t pid,
308 const FilePath &container_dir, 305 const FilePath &container_dir,
309 const FilePath &core_path, 306 const FilePath &core_path,
310 const FilePath &minidump_path) { 307 const FilePath &minidump_path) {
311 if (!CopyOffProcFiles(pid, container_dir)) { 308 if (!CopyOffProcFiles(pid, container_dir)) {
(...skipping 15 matching lines...) Expand all
327 } 324 }
328 325
329 return conversion_result; 326 return conversion_result;
330 } 327 }
331 328
332 bool UserCollector::ConvertAndEnqueueCrash(int pid, 329 bool UserCollector::ConvertAndEnqueueCrash(int pid,
333 const std::string &exec, 330 const std::string &exec,
334 bool *out_of_capacity) { 331 bool *out_of_capacity) {
335 FilePath crash_path; 332 FilePath crash_path;
336 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { 333 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
337 LogCollectionError("Unable to find/create process-specific crash path"); 334 logger_->LogError("Unable to find/create process-specific crash path");
338 return false; 335 return false;
339 } 336 }
340 337
341 // Directory like /tmp/crash_reporter.1234 which contains the 338 // Directory like /tmp/crash_reporter.1234 which contains the
342 // procfs entries and other temporary files used during conversion. 339 // procfs entries and other temporary files used during conversion.
343 FilePath container_dir = FilePath("/tmp").Append( 340 FilePath container_dir = FilePath("/tmp").Append(
344 StringPrintf("crash_reporter.%d", pid)); 341 StringPrintf("crash_reporter.%d", pid));
345 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); 342 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
346 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); 343 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
347 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 344 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
(...skipping 21 matching lines...) Expand all
369 file_util::Delete(core_path, false); 366 file_util::Delete(core_path, false);
370 } else { 367 } else {
371 logger_->LogInfo("Leaving core file at %s due to developer image", 368 logger_->LogInfo("Leaving core file at %s due to developer image",
372 core_path.value().c_str()); 369 core_path.value().c_str());
373 } 370 }
374 371
375 file_util::Delete(container_dir, true); 372 file_util::Delete(container_dir, true);
376 return true; 373 return true;
377 } 374 }
378 375
379 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { 376 bool UserCollector::HandleCrash(int signal, int pid,
377 const char *kernel_supplied_name,
378 const char *force_exec) {
380 CHECK(initialized_); 379 CHECK(initialized_);
381 std::string exec; 380 std::string exec;
382 if (force_exec) { 381 if (force_exec) {
383 exec.assign(force_exec); 382 exec.assign(force_exec);
384 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { 383 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
385 // If for some reason we don't have the base name, avoid completely 384 // If we cannot find the exec name, use the kernel supplied name.
386 // failing by indicating an unknown name. 385 // We don't always use the kernel's since it truncates the name to
387 exec = "unknown"; 386 // 16 characters.
387 if (!kernel_supplied_name)
388 kernel_supplied_name = "null";
389 exec = StringPrintf("supplied_%s", kernel_supplied_name);
388 } 390 }
389 391
390 // Allow us to test the crash reporting mechanism successfully even if 392 // Allow us to test the crash reporting mechanism successfully even if
391 // other parts of the system crash. 393 // other parts of the system crash.
392 if (!FLAGS_filter_in.empty() && 394 if (!FLAGS_filter_in.empty() &&
393 (FLAGS_filter_in == "none" || 395 (FLAGS_filter_in == "none" ||
394 FLAGS_filter_in != exec)) { 396 FLAGS_filter_in != exec)) {
395 // We use a different format message to make it more obvious in tests 397 // We use a different format message to make it more obvious in tests
396 // which crashes are test generated and which are real. 398 // which crashes are test generated and which are real.
397 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s", 399 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s",
(...skipping 16 matching lines...) Expand all
414 } 416 }
415 417
416 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", 418 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
417 exec.c_str(), pid, signal, handling_string); 419 exec.c_str(), pid, signal, handling_string);
418 420
419 if (feedback) { 421 if (feedback) {
420 count_crash_function_(); 422 count_crash_function_();
421 423
422 if (generate_diagnostics_) { 424 if (generate_diagnostics_) {
423 bool out_of_capacity = false; 425 bool out_of_capacity = false;
424 if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) { 426 logger_->set_accumulator(&error_log_);
427 bool convert_and_enqueue_result =
428 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
429 logger_->set_accumulator(NULL);
430 if (!convert_and_enqueue_result) {
425 if (!out_of_capacity) 431 if (!out_of_capacity)
426 EnqueueCollectionErrorLog(pid, exec); 432 EnqueueCollectionErrorLog(pid, exec);
427 return false; 433 return false;
428 } 434 }
429 } 435 }
430 } 436 }
431 437
432 return true; 438 return true;
433 } 439 }
OLDNEW
« system_logging.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