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 "crash-reporter/kernel_collector.h" | 5 #include "crash-reporter/kernel_collector.h" |
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 | 11 |
| 12 const char KernelCollector::kClearingSequence[] = " "; |
| 13 static const char kDefaultKernelStackSignature[] = |
| 14 "kernel-UnspecifiedStackSignature"; |
12 static const char kKernelExecName[] = "kernel"; | 15 static const char kKernelExecName[] = "kernel"; |
| 16 const pid_t kKernelPid = 0; |
| 17 static const char kKernelSignatureKey[] = "sig"; |
| 18 // Byte length of maximum human readable portion of a kernel crash signature. |
| 19 static const int kMaxHumanStringLength = 40; |
13 static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; | 20 static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; |
14 const pid_t kKernelPid = 0; | |
15 const uid_t kRootUid = 0; | 21 const uid_t kRootUid = 0; |
16 | 22 // Time in seconds from the final kernel log message for a call stack |
17 const char KernelCollector::kClearingSequence[] = " "; | 23 // to count towards the signature of the kcrash. |
| 24 static const int kSignatureTimestampWindow = 2; |
| 25 // Kernel log timestamp regular expression. |
| 26 static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); |
18 | 27 |
19 KernelCollector::KernelCollector() | 28 KernelCollector::KernelCollector() |
20 : is_enabled_(false), | 29 : is_enabled_(false), |
21 preserved_dump_path_(kPreservedDumpPath) { | 30 preserved_dump_path_(kPreservedDumpPath) { |
22 } | 31 } |
23 | 32 |
24 KernelCollector::~KernelCollector() { | 33 KernelCollector::~KernelCollector() { |
25 } | 34 } |
26 | 35 |
27 void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { | 36 void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 preserved_dump_path_, | 68 preserved_dump_path_, |
60 kClearingSequence, | 69 kClearingSequence, |
61 strlen(kClearingSequence)) != strlen(kClearingSequence)) { | 70 strlen(kClearingSequence)) != strlen(kClearingSequence)) { |
62 logger_->LogError("Failed to clear kernel crash dump"); | 71 logger_->LogError("Failed to clear kernel crash dump"); |
63 return false; | 72 return false; |
64 } | 73 } |
65 logger_->LogInfo("Cleared kernel crash diagnostics"); | 74 logger_->LogInfo("Cleared kernel crash diagnostics"); |
66 return true; | 75 return true; |
67 } | 76 } |
68 | 77 |
| 78 // Hash a string to a number. We define our own hash function to not |
| 79 // be dependent on a C++ library that might change. This function |
| 80 // uses basically the same approach as tr1/functional_hash.h but with |
| 81 // a larger prime number (16127 vs 131). |
| 82 static unsigned HashString(const std::string &input) { |
| 83 unsigned hash = 0; |
| 84 for (size_t i = 0; i < input.length(); ++i) |
| 85 hash = hash * 16127 + input[i]; |
| 86 return hash; |
| 87 } |
| 88 |
| 89 void KernelCollector::ProcessStackTrace( |
| 90 pcrecpp::StringPiece kernel_dump, |
| 91 bool print_diagnostics, |
| 92 unsigned *hash, |
| 93 float *last_stack_timestamp) { |
| 94 pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); |
| 95 pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$"); |
| 96 // Match lines such as the following and grab out "error_code". |
| 97 // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c |
| 98 pcrecpp::RE stack_entry_re(kTimestampRegex + |
| 99 " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)"); |
| 100 std::string line; |
| 101 std::string hashable; |
| 102 |
| 103 *hash = 0; |
| 104 *last_stack_timestamp = 0; |
| 105 |
| 106 while (line_re.FindAndConsume(&kernel_dump, &line)) { |
| 107 std::string certainty; |
| 108 std::string function_name; |
| 109 if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) { |
| 110 if (print_diagnostics) { |
| 111 printf("Stack trace starting. Clearing any prior traces.\n"); |
| 112 } |
| 113 hashable.clear(); |
| 114 } else if (stack_entry_re.PartialMatch(line, |
| 115 last_stack_timestamp, |
| 116 &certainty, |
| 117 &function_name)) { |
| 118 bool is_certain = certainty.find('?') == std::string::npos; |
| 119 if (print_diagnostics) { |
| 120 printf("@%f: stack entry for %s (%s)\n", |
| 121 *last_stack_timestamp, |
| 122 function_name.c_str(), |
| 123 is_certain ? "certain" : "uncertain"); |
| 124 } |
| 125 // Do not include any uncertain (prefixed by '?') frames in our hash. |
| 126 if (!is_certain) |
| 127 continue; |
| 128 if (!hashable.empty()) |
| 129 hashable.append("|"); |
| 130 hashable.append(function_name); |
| 131 } |
| 132 } |
| 133 |
| 134 *hash = HashString(hashable); |
| 135 |
| 136 if (print_diagnostics) { |
| 137 printf("Hash based on stack trace: \"%s\" at %f.\n", |
| 138 hashable.c_str(), *last_stack_timestamp); |
| 139 } |
| 140 } |
| 141 |
| 142 bool KernelCollector::FindCrashingFunction( |
| 143 pcrecpp::StringPiece kernel_dump, |
| 144 bool print_diagnostics, |
| 145 float stack_trace_timestamp, |
| 146 std::string *crashing_function) { |
| 147 pcrecpp::RE eip_re(kTimestampRegex + " EIP: \\[<.*>\\] ([^\\+ ]+).*", |
| 148 pcrecpp::MULTILINE()); |
| 149 float timestamp = 0; |
| 150 while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { |
| 151 if (print_diagnostics) { |
| 152 printf("@%f: found crashing function %s\n", |
| 153 timestamp, |
| 154 crashing_function->c_str()); |
| 155 } |
| 156 } |
| 157 if (timestamp == 0) { |
| 158 if (print_diagnostics) { |
| 159 printf("Found no crashing function.\n"); |
| 160 } |
| 161 return false; |
| 162 } |
| 163 if (stack_trace_timestamp != 0 && |
| 164 abs(stack_trace_timestamp - timestamp) > kSignatureTimestampWindow) { |
| 165 if (print_diagnostics) { |
| 166 printf("Found crashing function but not within window.\n"); |
| 167 } |
| 168 return false; |
| 169 } |
| 170 if (print_diagnostics) { |
| 171 printf("Found crashing function %s\n", crashing_function->c_str()); |
| 172 } |
| 173 return true; |
| 174 } |
| 175 |
| 176 bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump, |
| 177 bool print_diagnostics, |
| 178 std::string *panic_message) { |
| 179 // Match lines such as the following and grab out "Fatal exception" |
| 180 // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception |
| 181 pcrecpp::RE kernel_panic_re(kTimestampRegex + |
| 182 " Kernel panic[^\\:]*\\:\\s*(.*)", |
| 183 pcrecpp::MULTILINE()); |
| 184 float timestamp = 0; |
| 185 while (kernel_panic_re.FindAndConsume(&kernel_dump, |
| 186 ×tamp, |
| 187 panic_message)) { |
| 188 if (print_diagnostics) { |
| 189 printf("@%f: panic message %s\n", |
| 190 timestamp, |
| 191 panic_message->c_str()); |
| 192 } |
| 193 } |
| 194 if (timestamp == 0) { |
| 195 if (print_diagnostics) { |
| 196 printf("Found no panic message.\n"); |
| 197 } |
| 198 return false; |
| 199 } |
| 200 return true; |
| 201 } |
| 202 |
| 203 bool KernelCollector::ComputeKernelStackSignature( |
| 204 const std::string &kernel_dump, |
| 205 std::string *kernel_signature, |
| 206 bool print_diagnostics) { |
| 207 unsigned stack_hash = 0; |
| 208 float last_stack_timestamp = 0; |
| 209 std::string human_string; |
| 210 |
| 211 ProcessStackTrace(kernel_dump, |
| 212 print_diagnostics, |
| 213 &stack_hash, |
| 214 &last_stack_timestamp); |
| 215 |
| 216 if (!FindCrashingFunction(kernel_dump, |
| 217 print_diagnostics, |
| 218 last_stack_timestamp, |
| 219 &human_string)) { |
| 220 if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) { |
| 221 if (print_diagnostics) { |
| 222 printf("Found no human readable string, using empty string.\n"); |
| 223 } |
| 224 human_string.clear(); |
| 225 } |
| 226 } |
| 227 |
| 228 if (human_string.empty() && stack_hash == 0) { |
| 229 if (print_diagnostics) { |
| 230 printf("Found neither a stack nor a human readable string, failing.\n"); |
| 231 } |
| 232 return false; |
| 233 } |
| 234 |
| 235 human_string = human_string.substr(0, kMaxHumanStringLength); |
| 236 *kernel_signature = StringPrintf("%s-%s-%08X", |
| 237 kKernelExecName, |
| 238 human_string.c_str(), |
| 239 stack_hash); |
| 240 return true; |
| 241 } |
| 242 |
69 bool KernelCollector::Collect() { | 243 bool KernelCollector::Collect() { |
70 std::string kernel_dump; | 244 std::string kernel_dump; |
71 FilePath root_crash_directory; | 245 FilePath root_crash_directory; |
72 if (!LoadPreservedDump(&kernel_dump)) { | 246 if (!LoadPreservedDump(&kernel_dump)) { |
73 return false; | 247 return false; |
74 } | 248 } |
75 if (kernel_dump.empty()) { | 249 if (kernel_dump.empty()) { |
76 return false; | 250 return false; |
77 } | 251 } |
78 logger_->LogInfo("Received prior crash notification from kernel"); | 252 std::string signature; |
| 253 if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) { |
| 254 signature = kDefaultKernelStackSignature; |
| 255 } |
79 | 256 |
80 if (is_feedback_allowed_function_()) { | 257 bool feedback = is_feedback_allowed_function_(); |
| 258 |
| 259 logger_->LogInfo("Received prior crash notification from " |
| 260 "kernel (signature %s) (%s)", |
| 261 signature.c_str(), |
| 262 feedback ? "handling" : "ignoring"); |
| 263 |
| 264 if (feedback) { |
81 count_crash_function_(); | 265 count_crash_function_(); |
82 | 266 |
83 if (!GetCreatedCrashDirectoryByEuid(kRootUid, | 267 if (!GetCreatedCrashDirectoryByEuid(kRootUid, |
84 &root_crash_directory)) { | 268 &root_crash_directory)) { |
85 return true; | 269 return true; |
86 } | 270 } |
87 | 271 |
88 std::string dump_basename = | 272 std::string dump_basename = |
89 FormatDumpBasename(kKernelExecName, | 273 FormatDumpBasename(kKernelExecName, |
90 time(NULL), | 274 time(NULL), |
91 kKernelPid); | 275 kKernelPid); |
92 FilePath kernel_crash_path = root_crash_directory.Append( | 276 FilePath kernel_crash_path = root_crash_directory.Append( |
93 StringPrintf("%s.kcrash", dump_basename.c_str())); | 277 StringPrintf("%s.kcrash", dump_basename.c_str())); |
94 | 278 |
95 if (file_util::WriteFile(kernel_crash_path, | 279 if (file_util::WriteFile(kernel_crash_path, |
96 kernel_dump.data(), | 280 kernel_dump.data(), |
97 kernel_dump.length()) != | 281 kernel_dump.length()) != |
98 static_cast<int>(kernel_dump.length())) { | 282 static_cast<int>(kernel_dump.length())) { |
99 logger_->LogInfo("Failed to write kernel dump to %s", | 283 logger_->LogInfo("Failed to write kernel dump to %s", |
100 kernel_crash_path.value().c_str()); | 284 kernel_crash_path.value().c_str()); |
101 return true; | 285 return true; |
102 } | 286 } |
103 | 287 |
| 288 AddCrashMetaData(kKernelSignatureKey, signature); |
104 WriteCrashMetaData( | 289 WriteCrashMetaData( |
105 root_crash_directory.Append( | 290 root_crash_directory.Append( |
106 StringPrintf("%s.meta", dump_basename.c_str())), | 291 StringPrintf("%s.meta", dump_basename.c_str())), |
107 kKernelExecName, | 292 kKernelExecName, |
108 kernel_crash_path.value()); | 293 kernel_crash_path.value()); |
109 | 294 |
110 logger_->LogInfo("Collected kernel crash diagnostics into %s", | 295 logger_->LogInfo("Stored kcrash to %s", |
111 kernel_crash_path.value().c_str()); | 296 kernel_crash_path.value().c_str()); |
112 } else { | |
113 logger_->LogInfo("Crash not saved since metrics disabled"); | |
114 } | 297 } |
115 if (!ClearPreservedDump()) { | 298 if (!ClearPreservedDump()) { |
116 return false; | 299 return false; |
117 } | 300 } |
118 | 301 |
119 return true; | 302 return true; |
120 } | 303 } |
OLD | NEW |