Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(665)

Side by Side Diff: kernel_collector.cc

Issue 4018008: crash-reporter: Generate kernel crash signatures for server-side grouping of similar crashes (Closed) Base URL: http://git.chromium.org/git/crash-reporter.git
Patch Set: Respond to petkov review Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « kernel_collector.h ('k') | kernel_collector_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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, &timestamp, 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 &timestamp,
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 }
OLDNEW
« no previous file with comments | « kernel_collector.h ('k') | kernel_collector_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698