| Index: kernel_collector.cc
|
| diff --git a/kernel_collector.cc b/kernel_collector.cc
|
| index c6bb33fca4d3c664edfd1e688e3a851466d29a5b..a95d757dbbaffe2ba423fcd0ac796a4c10e0a876 100644
|
| --- a/kernel_collector.cc
|
| +++ b/kernel_collector.cc
|
| @@ -9,12 +9,21 @@
|
| #include "base/string_util.h"
|
| #include "crash-reporter/system_logging.h"
|
|
|
| +const char KernelCollector::kClearingSequence[] = " ";
|
| +static const char kDefaultKernelStackSignature[] =
|
| + "kernel-UnspecifiedStackSignature";
|
| static const char kKernelExecName[] = "kernel";
|
| -static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash";
|
| const pid_t kKernelPid = 0;
|
| +static const char kKernelSignatureKey[] = "sig";
|
| +// Byte length of maximum human readable portion of a kernel crash signature.
|
| +static const int kMaxHumanStringLength = 40;
|
| +static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash";
|
| const uid_t kRootUid = 0;
|
| -
|
| -const char KernelCollector::kClearingSequence[] = " ";
|
| +// Time in seconds from the final kernel log message for a call stack
|
| +// to count towards the signature of the kcrash.
|
| +static const int kSignatureTimestampWindow = 2;
|
| +// Kernel log timestamp regular expression.
|
| +static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]");
|
|
|
| KernelCollector::KernelCollector()
|
| : is_enabled_(false),
|
| @@ -66,6 +75,171 @@ bool KernelCollector::ClearPreservedDump() {
|
| return true;
|
| }
|
|
|
| +// Hash a string to a number. We define our own hash function to not
|
| +// be dependent on a C++ library that might change. This function
|
| +// uses basically the same approach as tr1/functional_hash.h but with
|
| +// a larger prime number (16127 vs 131).
|
| +static unsigned HashString(const std::string &input) {
|
| + unsigned hash = 0;
|
| + for (size_t i = 0; i < input.length(); ++i)
|
| + hash = hash * 16127 + input[i];
|
| + return hash;
|
| +}
|
| +
|
| +void KernelCollector::ProcessStackTrace(
|
| + pcrecpp::StringPiece kernel_dump,
|
| + bool print_diagnostics,
|
| + unsigned *hash,
|
| + float *last_stack_timestamp) {
|
| + pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
|
| + pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$");
|
| + // Match lines such as the following and grab out "error_code".
|
| + // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c
|
| + pcrecpp::RE stack_entry_re(kTimestampRegex +
|
| + " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)");
|
| + std::string line;
|
| + std::string hashable;
|
| +
|
| + *hash = 0;
|
| + *last_stack_timestamp = 0;
|
| +
|
| + while (line_re.FindAndConsume(&kernel_dump, &line)) {
|
| + std::string certainty;
|
| + std::string function_name;
|
| + if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
|
| + if (print_diagnostics) {
|
| + printf("Stack trace starting. Clearing any prior traces.\n");
|
| + }
|
| + hashable.clear();
|
| + } else if (stack_entry_re.PartialMatch(line,
|
| + last_stack_timestamp,
|
| + &certainty,
|
| + &function_name)) {
|
| + bool is_certain = certainty.find('?') == std::string::npos;
|
| + if (print_diagnostics) {
|
| + printf("@%f: stack entry for %s (%s)\n",
|
| + *last_stack_timestamp,
|
| + function_name.c_str(),
|
| + is_certain ? "certain" : "uncertain");
|
| + }
|
| + // Do not include any uncertain (prefixed by '?') frames in our hash.
|
| + if (!is_certain)
|
| + continue;
|
| + if (!hashable.empty())
|
| + hashable.append("|");
|
| + hashable.append(function_name);
|
| + }
|
| + }
|
| +
|
| + *hash = HashString(hashable);
|
| +
|
| + if (print_diagnostics) {
|
| + printf("Hash based on stack trace: \"%s\" at %f.\n",
|
| + hashable.c_str(), *last_stack_timestamp);
|
| + }
|
| +}
|
| +
|
| +bool KernelCollector::FindCrashingFunction(
|
| + pcrecpp::StringPiece kernel_dump,
|
| + bool print_diagnostics,
|
| + float stack_trace_timestamp,
|
| + std::string *crashing_function) {
|
| + pcrecpp::RE eip_re(kTimestampRegex + " EIP: \\[<.*>\\] ([^\\+ ]+).*",
|
| + pcrecpp::MULTILINE());
|
| + float timestamp = 0;
|
| + while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) {
|
| + if (print_diagnostics) {
|
| + printf("@%f: found crashing function %s\n",
|
| + timestamp,
|
| + crashing_function->c_str());
|
| + }
|
| + }
|
| + if (timestamp == 0) {
|
| + if (print_diagnostics) {
|
| + printf("Found no crashing function.\n");
|
| + }
|
| + return false;
|
| + }
|
| + if (stack_trace_timestamp != 0 &&
|
| + abs(stack_trace_timestamp - timestamp) > kSignatureTimestampWindow) {
|
| + if (print_diagnostics) {
|
| + printf("Found crashing function but not within window.\n");
|
| + }
|
| + return false;
|
| + }
|
| + if (print_diagnostics) {
|
| + printf("Found crashing function %s\n", crashing_function->c_str());
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
|
| + bool print_diagnostics,
|
| + std::string *panic_message) {
|
| + // Match lines such as the following and grab out "Fatal exception"
|
| + // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception
|
| + pcrecpp::RE kernel_panic_re(kTimestampRegex +
|
| + " Kernel panic[^\\:]*\\:\\s*(.*)",
|
| + pcrecpp::MULTILINE());
|
| + float timestamp = 0;
|
| + while (kernel_panic_re.FindAndConsume(&kernel_dump,
|
| + ×tamp,
|
| + panic_message)) {
|
| + if (print_diagnostics) {
|
| + printf("@%f: panic message %s\n",
|
| + timestamp,
|
| + panic_message->c_str());
|
| + }
|
| + }
|
| + if (timestamp == 0) {
|
| + if (print_diagnostics) {
|
| + printf("Found no panic message.\n");
|
| + }
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool KernelCollector::ComputeKernelStackSignature(
|
| + const std::string &kernel_dump,
|
| + std::string *kernel_signature,
|
| + bool print_diagnostics) {
|
| + unsigned stack_hash = 0;
|
| + float last_stack_timestamp = 0;
|
| + std::string human_string;
|
| +
|
| + ProcessStackTrace(kernel_dump,
|
| + print_diagnostics,
|
| + &stack_hash,
|
| + &last_stack_timestamp);
|
| +
|
| + if (!FindCrashingFunction(kernel_dump,
|
| + print_diagnostics,
|
| + last_stack_timestamp,
|
| + &human_string)) {
|
| + if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
|
| + if (print_diagnostics) {
|
| + printf("Found no human readable string, using empty string.\n");
|
| + }
|
| + human_string.clear();
|
| + }
|
| + }
|
| +
|
| + if (human_string.empty() && stack_hash == 0) {
|
| + if (print_diagnostics) {
|
| + printf("Found neither a stack nor a human readable string, failing.\n");
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + human_string = human_string.substr(0, kMaxHumanStringLength);
|
| + *kernel_signature = StringPrintf("%s-%s-%08X",
|
| + kKernelExecName,
|
| + human_string.c_str(),
|
| + stack_hash);
|
| + return true;
|
| +}
|
| +
|
| bool KernelCollector::Collect() {
|
| std::string kernel_dump;
|
| FilePath root_crash_directory;
|
| @@ -75,9 +249,19 @@ bool KernelCollector::Collect() {
|
| if (kernel_dump.empty()) {
|
| return false;
|
| }
|
| - logger_->LogInfo("Received prior crash notification from kernel");
|
| + std::string signature;
|
| + if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
|
| + signature = kDefaultKernelStackSignature;
|
| + }
|
| +
|
| + bool feedback = is_feedback_allowed_function_();
|
| +
|
| + logger_->LogInfo("Received prior crash notification from "
|
| + "kernel (signature %s) (%s)",
|
| + signature.c_str(),
|
| + feedback ? "handling" : "ignoring");
|
|
|
| - if (is_feedback_allowed_function_()) {
|
| + if (feedback) {
|
| count_crash_function_();
|
|
|
| if (!GetCreatedCrashDirectoryByEuid(kRootUid,
|
| @@ -101,16 +285,15 @@ bool KernelCollector::Collect() {
|
| return true;
|
| }
|
|
|
| + AddCrashMetaData(kKernelSignatureKey, signature);
|
| WriteCrashMetaData(
|
| root_crash_directory.Append(
|
| StringPrintf("%s.meta", dump_basename.c_str())),
|
| kKernelExecName,
|
| kernel_crash_path.value());
|
|
|
| - logger_->LogInfo("Collected kernel crash diagnostics into %s",
|
| + logger_->LogInfo("Stored kcrash to %s",
|
| kernel_crash_path.value().c_str());
|
| - } else {
|
| - logger_->LogInfo("Crash not saved since metrics disabled");
|
| }
|
| if (!ClearPreservedDump()) {
|
| return false;
|
|
|