| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include "base/file_util.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| #include "crash-reporter/kernel_collector.h" |
| #include "crash-reporter/system_logging.h" |
| #include "crash-reporter/unclean_shutdown_collector.h" |
| #include "crash-reporter/user_collector.h" |
| #include "gflags/gflags.h" |
| #include "metrics/metrics_library.h" |
| |
| #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
| DEFINE_bool(init, false, "Initialize crash logging"); |
| DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); |
| DEFINE_string(generate_kernel_signature, "", |
| "Generate signature from given kcrash file"); |
| DEFINE_bool(crash_test, false, "Crash test"); |
| DEFINE_int32(pid, -1, "Crashing PID"); |
| DEFINE_int32(signal, -1, "Signal causing crash"); |
| DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); |
| #pragma GCC diagnostic error "-Wstrict-aliasing" |
| |
| static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; |
| static const char kUserCrashSignal[] = |
| "org.chromium.CrashReporter.UserCrash"; |
| static const char kUncleanShutdownFile[] = |
| "/var/lib/crash_reporter/pending_clean_shutdown"; |
| |
| |
| // Enumeration of kinds of crashes to be used in the CrashCounter histogram. |
| enum CrashKinds { |
| kCrashKindUncleanShutdown = 1, |
| kCrashKindUser = 2, |
| kCrashKindKernel = 3, |
| kCrashKindMax |
| }; |
| |
| static MetricsLibrary s_metrics_lib; |
| static SystemLoggingImpl s_system_log; |
| |
| static bool IsFeedbackAllowed() { |
| return s_metrics_lib.AreMetricsEnabled(); |
| } |
| |
| static bool TouchFile(const FilePath &file_path) { |
| return file_util::WriteFile(file_path, "", 0) == 0; |
| } |
| |
| static void CountKernelCrash() { |
| s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), |
| kCrashKindKernel, |
| kCrashKindMax); |
| } |
| |
| static void CountUncleanShutdown() { |
| s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), |
| kCrashKindUncleanShutdown, |
| kCrashKindMax); |
| } |
| |
| static void CountUserCrash() { |
| s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), |
| kCrashKindUser, |
| kCrashKindMax); |
| std::string command = StringPrintf( |
| "/usr/bin/dbus-send --type=signal --system / \"%s\" &", |
| kUserCrashSignal); |
| // Announce through D-Bus whenever a user crash happens. This is |
| // used by the metrics daemon to log active use time between |
| // crashes. |
| // |
| // This could be done more efficiently by explicit fork/exec or |
| // using a dbus library directly. However, this should run |
| // relatively rarely and longer term we may need to implement a |
| // better way to do this that doesn't rely on D-Bus. |
| // |
| // We run in the background in case dbus daemon itself is crashed |
| // and not responding. This allows us to not block and potentially |
| // deadlock on a dbus-daemon crash. If dbus-daemon crashes without |
| // restarting, each crash will fork off a lot of dbus-send |
| // processes. Such a system is in a unusable state and will need |
| // to be restarted anyway. |
| |
| int status = system(command.c_str()); |
| if (status != 0) { |
| s_system_log.LogWarning("dbus-send running failed"); |
| } |
| } |
| |
| static int Initialize(KernelCollector *kernel_collector, |
| UserCollector *user_collector, |
| UncleanShutdownCollector *unclean_shutdown_collector) { |
| CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; |
| |
| bool was_kernel_crash = false; |
| bool was_unclean_shutdown = false; |
| kernel_collector->Enable(); |
| if (kernel_collector->IsEnabled()) { |
| was_kernel_crash = kernel_collector->Collect(); |
| } |
| |
| if (FLAGS_unclean_check) { |
| was_unclean_shutdown = unclean_shutdown_collector->Collect(); |
| } |
| |
| // Touch a file to notify the metrics daemon that a kernel |
| // crash has been detected so that it can log the time since |
| // the last kernel crash. |
| if (IsFeedbackAllowed()) { |
| if (was_kernel_crash) { |
| TouchFile(FilePath("/tmp/kernel-crash-detected")); |
| } else if (was_unclean_shutdown) { |
| // We only count an unclean shutdown if it did not come with |
| // an associated kernel crash. |
| TouchFile(FilePath("/tmp/unclean-shutdown-detected")); |
| } |
| } |
| |
| // Must enable the unclean shutdown collector *after* collecting. |
| unclean_shutdown_collector->Enable(); |
| user_collector->Enable(); |
| |
| return 0; |
| } |
| |
| static int HandleUserCrash(UserCollector *user_collector) { |
| // Handle a specific user space crash. |
| CHECK(FLAGS_signal != -1) << "Signal must be set"; |
| CHECK(FLAGS_pid != -1) << "PID must be set"; |
| |
| // Make it possible to test what happens when we crash while |
| // handling a crash. |
| if (FLAGS_crash_test) { |
| *(char *)0 = 0; |
| return 0; |
| } |
| |
| // Handle the crash, get the name of the process from procfs. |
| if (!user_collector->HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| // Interactive/diagnostics mode for generating kernel crash signatures. |
| static int GenerateKernelSignature(KernelCollector *kernel_collector) { |
| std::string kcrash_contents; |
| std::string signature; |
| if (!file_util::ReadFileToString(FilePath(FLAGS_generate_kernel_signature), |
| &kcrash_contents)) { |
| fprintf(stderr, "Could not read file.\n"); |
| return 1; |
| } |
| if (!kernel_collector->ComputeKernelStackSignature( |
| kcrash_contents, |
| &signature, |
| true)) { |
| fprintf(stderr, "Signature could not be generated.\n"); |
| return 1; |
| } |
| printf("Kernel crash signature is \"%s\".\n", signature.c_str()); |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) { |
| google::ParseCommandLineFlags(&argc, &argv, true); |
| FilePath my_path(argv[0]); |
| file_util::AbsolutePath(&my_path); |
| s_metrics_lib.Init(); |
| CommandLine::Init(argc, argv); |
| logging::InitLogging(NULL, |
| logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, |
| logging::DONT_LOCK_LOG_FILE, |
| logging::DELETE_OLD_LOG_FILE); |
| s_system_log.Initialize(my_path.BaseName().value().c_str()); |
| KernelCollector kernel_collector; |
| kernel_collector.Initialize(CountKernelCrash, |
| IsFeedbackAllowed, |
| &s_system_log); |
| UserCollector user_collector; |
| user_collector.Initialize(CountUserCrash, |
| my_path.value(), |
| IsFeedbackAllowed, |
| &s_system_log, |
| true); // generate_diagnostics |
| UncleanShutdownCollector unclean_shutdown_collector; |
| unclean_shutdown_collector.Initialize(CountUncleanShutdown, |
| IsFeedbackAllowed, |
| &s_system_log); |
| |
| if (FLAGS_init) { |
| return Initialize(&kernel_collector, |
| &user_collector, |
| &unclean_shutdown_collector); |
| } |
| |
| if (FLAGS_clean_shutdown) { |
| unclean_shutdown_collector.Disable(); |
| user_collector.Disable(); |
| return 0; |
| } |
| |
| if (!FLAGS_generate_kernel_signature.empty()) { |
| return GenerateKernelSignature(&kernel_collector); |
| } |
| |
| return HandleUserCrash(&user_collector); |
| } |