/* * 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 // for open #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }