| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <fcntl.h> // for open |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_util.h> |
| #include <base/guid.h> |
| #include <base/logging.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <binder/IServiceManager.h> |
| #include <brillo/flag_helper.h> |
| #include <brillo/syslog_logging.h> |
| #include <metrics/metrics_collector_service_client.h> |
| #include <metrics/metrics_library.h> |
| #include <utils/String16.h> |
| |
| |
| #include "kernel_collector.h" |
| #include "kernel_warning_collector.h" |
| #include "unclean_shutdown_collector.h" |
| #include "user_collector.h" |
| |
| #if !defined(__ANDROID__) |
| #include "udev_collector.h" |
| #endif |
| |
| static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; |
| static const char kKernelCrashDetected[] = |
| "/data/misc/crash_reporter/run/kernel-crash-detected"; |
| static const char kUncleanShutdownDetected[] = |
| "/var/run/unclean-shutdown-detected"; |
| static const char kGUIDFileName[] = "/data/misc/crash_reporter/guid"; |
| |
| // Enumeration of kinds of crashes to be used in the CrashCounter histogram. |
| enum CrashKinds { |
| kCrashKindUncleanShutdown = 1, |
| kCrashKindUser = 2, |
| kCrashKindKernel = 3, |
| kCrashKindUdev = 4, |
| kCrashKindKernelWarning = 5, |
| kCrashKindMax |
| }; |
| |
| static MetricsLibrary s_metrics_lib; |
| |
| using android::brillo::metrics::IMetricsCollectorService; |
| using base::FilePath; |
| using base::StringPrintf; |
| |
| static bool IsFeedbackAllowed() { |
| return s_metrics_lib.AreMetricsEnabled(); |
| } |
| |
| static bool TouchFile(const FilePath &file_path) { |
| return base::WriteFile(file_path, "", 0) == 0; |
| } |
| |
| static void SendCrashMetrics(CrashKinds type, const char* name) { |
| // TODO(kmixter): We can remove this histogram as part of |
| // crosbug.com/11163. |
| s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax); |
| s_metrics_lib.SendCrashToUMA(name); |
| } |
| |
| static void CountKernelCrash() { |
| SendCrashMetrics(kCrashKindKernel, "kernel"); |
| } |
| |
| static void CountUdevCrash() { |
| SendCrashMetrics(kCrashKindUdev, "udevcrash"); |
| } |
| |
| static void CountUncleanShutdown() { |
| SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown"); |
| } |
| |
| static void CountUserCrash() { |
| SendCrashMetrics(kCrashKindUser, "user"); |
| // Tell the metrics collector about the user crash, in order to log active |
| // use time between crashes. |
| MetricsCollectorServiceClient metrics_collector_service; |
| |
| if (metrics_collector_service.Init()) |
| metrics_collector_service.notifyUserCrash(); |
| else |
| LOG(ERROR) << "Failed to send user crash notification to metrics_collector"; |
| } |
| |
| |
| static int Initialize(KernelCollector *kernel_collector, |
| UserCollector *user_collector, |
| UncleanShutdownCollector *unclean_shutdown_collector, |
| const bool unclean_check, |
| const bool clean_shutdown) { |
| CHECK(!clean_shutdown) << "Incompatible options"; |
| |
| // Try to read the GUID from kGUIDFileName. If the file doesn't exist, is |
| // blank, or the read fails, generate a new GUID and write it to the file. |
| std::string guid; |
| base::FilePath filepath(kGUIDFileName); |
| if (!base::ReadFileToString(filepath, &guid) || guid.empty()) { |
| guid = base::GenerateGUID(); |
| // If we can't read or write the file, log an error. However it is not |
| // a fatal error, as the crash server will assign a random GUID based |
| // on a hash of the IP address if one is not provided in the report. |
| if (base::WriteFile(filepath, guid.c_str(), guid.size()) <= 0) { |
| LOG(ERROR) << "Could not write guid " << guid << " to file " |
| << filepath.value(); |
| } |
| } |
| |
| bool was_kernel_crash = false; |
| bool was_unclean_shutdown = false; |
| kernel_collector->Enable(); |
| if (kernel_collector->is_enabled()) { |
| was_kernel_crash = kernel_collector->Collect(); |
| } |
| |
| if (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(kKernelCrashDetected)); |
| } else if (was_unclean_shutdown) { |
| // We only count an unclean shutdown if it did not come with |
| // an associated kernel crash. |
| TouchFile(FilePath(kUncleanShutdownDetected)); |
| } |
| } |
| |
| // Must enable the unclean shutdown collector *after* collecting. |
| unclean_shutdown_collector->Enable(); |
| user_collector->Enable(); |
| |
| return 0; |
| } |
| |
| static int HandleUserCrash(UserCollector *user_collector, |
| const std::string& user, const bool crash_test) { |
| // Handle a specific user space crash. |
| CHECK(!user.empty()) << "--user= must be set"; |
| |
| // Make it possible to test what happens when we crash while |
| // handling a crash. |
| if (crash_test) { |
| *(volatile char *)0 = 0; |
| return 0; |
| } |
| |
| // Accumulate logs to help in diagnosing failures during user collection. |
| brillo::LogToString(true); |
| // Handle the crash, get the name of the process from procfs. |
| bool handled = user_collector->HandleCrash(user, nullptr); |
| brillo::LogToString(false); |
| if (!handled) |
| return 1; |
| return 0; |
| } |
| |
| #if !defined(__ANDROID__) |
| static int HandleUdevCrash(UdevCollector *udev_collector, |
| const std::string& udev_event) { |
| // Handle a crash indicated by a udev event. |
| CHECK(!udev_event.empty()) << "--udev= must be set"; |
| |
| // Accumulate logs to help in diagnosing failures during user collection. |
| brillo::LogToString(true); |
| bool handled = udev_collector->HandleCrash(udev_event); |
| brillo::LogToString(false); |
| if (!handled) |
| return 1; |
| return 0; |
| } |
| #endif |
| |
| static int HandleKernelWarning(KernelWarningCollector |
| *kernel_warning_collector) { |
| // Accumulate logs to help in diagnosing failures during collection. |
| brillo::LogToString(true); |
| bool handled = kernel_warning_collector->Collect(); |
| brillo::LogToString(false); |
| if (!handled) |
| return 1; |
| return 0; |
| } |
| |
| // Interactive/diagnostics mode for generating kernel crash signatures. |
| static int GenerateKernelSignature(KernelCollector *kernel_collector, |
| const std::string& kernel_signature_file) { |
| std::string kcrash_contents; |
| std::string signature; |
| if (!base::ReadFileToString(FilePath(kernel_signature_file), |
| &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; |
| } |
| |
| // Ensure stdout, stdin, and stderr are open file descriptors. If |
| // they are not, any code which writes to stderr/stdout may write out |
| // to files opened during execution. In particular, when |
| // crash_reporter is run by the kernel coredump pipe handler (via |
| // kthread_create/kernel_execve), it will not have file table entries |
| // 1 and 2 (stdout and stderr) populated. We populate them here. |
| static void OpenStandardFileDescriptors() { |
| int new_fd = -1; |
| // We open /dev/null to fill in any of the standard [0, 2] file |
| // descriptors. We leave these open for the duration of the |
| // process. This works because open returns the lowest numbered |
| // invalid fd. |
| do { |
| new_fd = open("/dev/null", 0); |
| CHECK_GE(new_fd, 0) << "Unable to open /dev/null"; |
| } while (new_fd >= 0 && new_fd <= 2); |
| close(new_fd); |
| } |
| |
| int main(int argc, char *argv[]) { |
| 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_string(user, "", "User crash info (pid:signal:exec_name)"); |
| DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); |
| |
| #if !defined(__ANDROID__) |
| DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); |
| #endif |
| |
| DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); |
| DEFINE_string(pid, "", "PID of crashing process"); |
| DEFINE_string(uid, "", "UID of crashing process"); |
| DEFINE_string(exe, "", "Executable name of crashing process"); |
| DEFINE_bool(core2md_failure, false, "Core2md failure test"); |
| DEFINE_bool(directory_failure, false, "Spool directory failure test"); |
| DEFINE_string(filter_in, "", |
| "Ignore all crashes but this for testing"); |
| |
| OpenStandardFileDescriptors(); |
| FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0])); |
| s_metrics_lib.Init(); |
| brillo::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter"); |
| brillo::OpenLog(my_path.BaseName().value().c_str(), true); |
| brillo::InitLog(brillo::kLogToSyslog); |
| |
| KernelCollector kernel_collector; |
| kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed); |
| UserCollector user_collector; |
| user_collector.Initialize(CountUserCrash, |
| my_path.value(), |
| IsFeedbackAllowed, |
| true, // generate_diagnostics |
| FLAGS_core2md_failure, |
| FLAGS_directory_failure, |
| FLAGS_filter_in); |
| UncleanShutdownCollector unclean_shutdown_collector; |
| unclean_shutdown_collector.Initialize(CountUncleanShutdown, |
| IsFeedbackAllowed); |
| |
| #if !defined(__ANDROID__) |
| UdevCollector udev_collector; |
| udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); |
| #endif |
| |
| KernelWarningCollector kernel_warning_collector; |
| kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); |
| |
| if (FLAGS_init) { |
| return Initialize(&kernel_collector, |
| &user_collector, |
| &unclean_shutdown_collector, |
| FLAGS_unclean_check, |
| FLAGS_clean_shutdown); |
| } |
| |
| if (FLAGS_clean_shutdown) { |
| unclean_shutdown_collector.Disable(); |
| user_collector.Disable(); |
| return 0; |
| } |
| |
| if (!FLAGS_generate_kernel_signature.empty()) { |
| return GenerateKernelSignature(&kernel_collector, |
| FLAGS_generate_kernel_signature); |
| } |
| |
| #if !defined(__ANDROID__) |
| if (!FLAGS_udev.empty()) { |
| return HandleUdevCrash(&udev_collector, FLAGS_udev); |
| } |
| #endif |
| |
| if (FLAGS_kernel_warning) { |
| return HandleKernelWarning(&kernel_warning_collector); |
| } |
| |
| return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test); |
| } |