| OLD | NEW |
| 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 <string> | 5 #include <string> |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "crash-reporter/system_logging.h" | 10 #include "crash-reporter/system_logging.h" |
| 11 #include "crash-reporter/user_collector.h" | 11 #include "crash-reporter/user_collector.h" |
| 12 #include "gflags/gflags.h" | 12 #include "gflags/gflags.h" |
| 13 #include "metrics/metrics_library.h" | 13 #include "metrics/metrics_library.h" |
| 14 | 14 |
| 15 #pragma GCC diagnostic ignored "-Wstrict-aliasing" | 15 #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
| 16 DEFINE_bool(init, false, "Initialize crash logging"); | 16 DEFINE_bool(init, false, "Initialize crash logging"); |
| 17 DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); | 17 DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); |
| 18 DEFINE_bool(crash_test, false, "Crash test"); | 18 DEFINE_bool(crash_test, false, "Crash test"); |
| 19 DEFINE_string(exec, "", "Executable name crashed"); | |
| 20 DEFINE_int32(pid, -1, "Crashing PID"); | 19 DEFINE_int32(pid, -1, "Crashing PID"); |
| 21 DEFINE_int32(signal, -1, "Signal causing crash"); | 20 DEFINE_int32(signal, -1, "Signal causing crash"); |
| 22 DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); | 21 DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); |
| 23 #pragma GCC diagnostic error "-Wstrict-aliasing" | 22 #pragma GCC diagnostic error "-Wstrict-aliasing" |
| 24 | 23 |
| 25 static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; | 24 static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; |
| 25 static const char kEmpty[] = ""; |
| 26 static const char kUncleanShutdownFile[] = | 26 static const char kUncleanShutdownFile[] = |
| 27 "/var/lib/crash_reporter/pending_clean_shutdown"; | 27 "/var/lib/crash_reporter/pending_clean_shutdown"; |
| 28 static const char kEmpty[] = ""; | 28 |
| 29 | 29 |
| 30 // Enumeration of kinds of crashes to be used in the CrashCounter histogram. | 30 // Enumeration of kinds of crashes to be used in the CrashCounter histogram. |
| 31 enum CrashKinds { | 31 enum CrashKinds { |
| 32 CRASH_KIND_KERNEL = 1, | 32 kCrashKindKernel = 1, |
| 33 CRASH_KIND_USER = 2, | 33 kCrashKindUser = 2, |
| 34 CRASH_KIND_MAX | 34 kCrashKindMax |
| 35 }; | 35 }; |
| 36 | 36 |
| 37 static MetricsLibrary s_metrics_lib; | 37 static MetricsLibrary s_metrics_lib; |
| 38 static SystemLoggingImpl s_system_log; | 38 static SystemLoggingImpl s_system_log; |
| 39 | 39 |
| 40 static bool IsMetricsCollectionAllowed() { | 40 static bool IsMetricsCollectionAllowed() { |
| 41 // TODO(kmixter): Eventually check system tainted state and | 41 // TODO(kmixter): Eventually check system tainted state and |
| 42 // move this down in metrics library where it would be explicitly | 42 // move this down in metrics library where it would be explicitly |
| 43 // checked when asked to send stats. | 43 // checked when asked to send stats. |
| 44 return true; | 44 return true; |
| 45 } | 45 } |
| 46 | 46 |
| 47 static void CheckUncleanShutdown() { | 47 static void CheckUncleanShutdown() { |
| 48 FilePath unclean_file_path(kUncleanShutdownFile); | 48 FilePath unclean_file_path(kUncleanShutdownFile); |
| 49 if (!file_util::PathExists(unclean_file_path)) { | 49 if (!file_util::PathExists(unclean_file_path)) { |
| 50 return; | 50 return; |
| 51 } | 51 } |
| 52 s_system_log.LogWarning("Last shutdown was not clean"); | 52 s_system_log.LogWarning("Last shutdown was not clean"); |
| 53 if (IsMetricsCollectionAllowed()) { | 53 if (IsMetricsCollectionAllowed()) { |
| 54 s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), | 54 s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), |
| 55 CRASH_KIND_KERNEL, | 55 kCrashKindKernel, |
| 56 CRASH_KIND_MAX); | 56 kCrashKindMax); |
| 57 } | 57 } |
| 58 if (!file_util::Delete(unclean_file_path, false)) { | 58 if (!file_util::Delete(unclean_file_path, false)) { |
| 59 s_system_log.LogError("Failed to delete unclean shutdown file %s", | 59 s_system_log.LogError("Failed to delete unclean shutdown file %s", |
| 60 kUncleanShutdownFile); | 60 kUncleanShutdownFile); |
| 61 } | 61 } |
| 62 | 62 |
| 63 // Touch a file to notify the metrics daemon that a kernel crash has | 63 // Touch a file to notify the metrics daemon that a kernel crash has |
| 64 // been detected so that it can log the time since the last kernel | 64 // been detected so that it can log the time since the last kernel |
| 65 // crash. | 65 // crash. |
| 66 static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; | 66 static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; |
| 67 FilePath crash_detected(kKernelCrashDetectedFile); | 67 FilePath crash_detected(kKernelCrashDetectedFile); |
| 68 file_util::WriteFile(crash_detected, kEmpty, 0); | 68 file_util::WriteFile(crash_detected, kEmpty, 0); |
| 69 } | 69 } |
| 70 | 70 |
| 71 static bool PrepareUncleanShutdownCheck() { | 71 static bool PrepareUncleanShutdownCheck() { |
| 72 FilePath file_path(kUncleanShutdownFile); | 72 FilePath file_path(kUncleanShutdownFile); |
| 73 file_util::CreateDirectory(file_path.DirName()); | 73 file_util::CreateDirectory(file_path.DirName()); |
| 74 return file_util::WriteFile(file_path, kEmpty, 0) == 0; | 74 return file_util::WriteFile(file_path, kEmpty, 0) == 0; |
| 75 } | 75 } |
| 76 | 76 |
| 77 static void SignalCleanShutdown() { | 77 static void SignalCleanShutdown() { |
| 78 s_system_log.LogInfo("Clean shutdown signalled"); | 78 s_system_log.LogInfo("Clean shutdown signalled"); |
| 79 file_util::Delete(FilePath(kUncleanShutdownFile), false); | 79 file_util::Delete(FilePath(kUncleanShutdownFile), false); |
| 80 } | 80 } |
| 81 | 81 |
| 82 static void CountUserCrash() { | 82 static void CountUserCrash() { |
| 83 CHECK(IsMetricsCollectionAllowed()); | 83 CHECK(IsMetricsCollectionAllowed()); |
| 84 s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), | 84 s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), |
| 85 CRASH_KIND_USER, | 85 kCrashKindUser, |
| 86 CRASH_KIND_MAX); | 86 kCrashKindMax); |
| 87 | 87 |
| 88 // Announce through D-Bus whenever a user crash happens. This is | 88 // Announce through D-Bus whenever a user crash happens. This is |
| 89 // used by the metrics daemon to log active use time between | 89 // used by the metrics daemon to log active use time between |
| 90 // crashes. | 90 // crashes. |
| 91 // | 91 // |
| 92 // This could be done more efficiently by explicit fork/exec or | 92 // This could be done more efficiently by explicit fork/exec or |
| 93 // using a dbus library directly. However, this should run | 93 // using a dbus library directly. However, this should run |
| 94 // relatively rarely and longer term we may need to implement a | 94 // relatively rarely and longer term we may need to implement a |
| 95 // better way to do this that doesn't rely on D-Bus. | 95 // better way to do this that doesn't rely on D-Bus. |
| 96 int status __attribute__((unused)) = | 96 int status __attribute__((unused)) = |
| 97 system("/usr/bin/dbus-send --type=signal --system / " | 97 system("/usr/bin/dbus-send --type=signal --system / " |
| 98 "org.chromium.CrashReporter.UserCrash"); | 98 "org.chromium.CrashReporter.UserCrash"); |
| 99 } | 99 } |
| 100 | 100 |
| 101 int main(int argc, char *argv[]) { | 101 int main(int argc, char *argv[]) { |
| 102 google::ParseCommandLineFlags(&argc, &argv, true); | 102 google::ParseCommandLineFlags(&argc, &argv, true); |
| 103 FilePath my_path(argv[0]); | 103 FilePath my_path(argv[0]); |
| 104 file_util::AbsolutePath(&my_path); | 104 file_util::AbsolutePath(&my_path); |
| 105 s_metrics_lib.Init(); | 105 s_metrics_lib.Init(); |
| 106 s_system_log.Initialize(my_path.BaseName().value().c_str()); | 106 s_system_log.Initialize(my_path.BaseName().value().c_str()); |
| 107 UserCollector user_collector; | 107 UserCollector user_collector; |
| 108 user_collector.Initialize(CountUserCrash, | 108 user_collector.Initialize(CountUserCrash, |
| 109 my_path.value(), | 109 my_path.value(), |
| 110 IsMetricsCollectionAllowed, | 110 IsMetricsCollectionAllowed, |
| 111 &s_system_log); | 111 &s_system_log, |
| 112 true); // generate_diagnostics |
| 112 | 113 |
| 113 if (FLAGS_init) { | 114 if (FLAGS_init) { |
| 114 CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; | 115 CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; |
| 115 user_collector.Enable(); | 116 user_collector.Enable(); |
| 116 if (FLAGS_unclean_check) { | 117 if (FLAGS_unclean_check) { |
| 117 CheckUncleanShutdown(); | 118 CheckUncleanShutdown(); |
| 118 if (!PrepareUncleanShutdownCheck()) { | 119 if (!PrepareUncleanShutdownCheck()) { |
| 119 s_system_log.LogError("Unable to create shutdown check file"); | 120 s_system_log.LogError("Unable to create shutdown check file"); |
| 120 } | 121 } |
| 121 } | 122 } |
| 122 return 0; | 123 return 0; |
| 123 } | 124 } |
| 124 | 125 |
| 125 if (FLAGS_clean_shutdown) { | 126 if (FLAGS_clean_shutdown) { |
| 126 SignalCleanShutdown(); | 127 SignalCleanShutdown(); |
| 127 user_collector.Disable(); | 128 user_collector.Disable(); |
| 128 return 0; | 129 return 0; |
| 129 } | 130 } |
| 130 | 131 |
| 131 // Handle a specific user space crash. | 132 // Handle a specific user space crash. |
| 132 CHECK(FLAGS_signal != -1) << "Signal must be set"; | 133 CHECK(FLAGS_signal != -1) << "Signal must be set"; |
| 133 CHECK(FLAGS_pid != -1) << "PID must be set"; | 134 CHECK(FLAGS_pid != -1) << "PID must be set"; |
| 134 CHECK(FLAGS_exec != "") << "Executable name must be set"; | |
| 135 | 135 |
| 136 // Make it possible to test what happens when we crash while | 136 // Make it possible to test what happens when we crash while |
| 137 // handling a crash. | 137 // handling a crash. |
| 138 if (FLAGS_crash_test) { | 138 if (FLAGS_crash_test) { |
| 139 *(char *)0 = 0; | 139 *(char *)0 = 0; |
| 140 return 0; | 140 return 0; |
| 141 } | 141 } |
| 142 | 142 |
| 143 user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, FLAGS_exec); | 143 // Handle the crash, get the name of the process from procfs. |
| 144 if (!user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { |
| 145 return 1; |
| 146 } |
| 144 | 147 |
| 145 return 0; | 148 return 0; |
| 146 } | 149 } |
| OLD | NEW |