blob: ab7e250fe487de665235527ef6dfb2a20a68c17b [file] [log] [blame]
Mike Frysinger57b261c2012-04-11 14:47:09 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Ken Mixter03403162010-08-18 15:23:16 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crash-reporter/kernel_collector.h"
6
Ben Chan7e776902014-06-18 13:19:51 -07007#include <map>
8
Ben Chanab6cc902014-09-05 08:21:06 -07009#include <base/files/file_util.h>
Ben Chan7e776902014-06-18 13:19:51 -070010#include <base/logging.h>
11#include <base/strings/string_util.h>
12#include <base/strings/stringprintf.h>
Ken Mixter03403162010-08-18 15:23:16 -070013
Ben Chan3c6b82c2014-07-23 14:52:14 -070014using base::FilePath;
15using base::StringPrintf;
16
17namespace {
18
19const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature";
20const char kDumpPath[] = "/dev/pstore";
21const char kDumpFormat[] = "dmesg-ramoops-%zu";
22const char kKernelExecName[] = "kernel";
Kees Cookce9556e2011-11-04 20:49:09 +000023// Maximum number of records to examine in the kDumpPath.
Ben Chan3c6b82c2014-07-23 14:52:14 -070024const size_t kMaxDumpRecords = 100;
Ken Mixterafcf8082010-10-26 14:45:01 -070025const pid_t kKernelPid = 0;
Ben Chan3c6b82c2014-07-23 14:52:14 -070026const char kKernelSignatureKey[] = "sig";
Ken Mixterafcf8082010-10-26 14:45:01 -070027// Byte length of maximum human readable portion of a kernel crash signature.
Ben Chan3c6b82c2014-07-23 14:52:14 -070028const int kMaxHumanStringLength = 40;
Ken Mixterafcf8082010-10-26 14:45:01 -070029const uid_t kRootUid = 0;
30// Time in seconds from the final kernel log message for a call stack
31// to count towards the signature of the kcrash.
Ben Chan3c6b82c2014-07-23 14:52:14 -070032const int kSignatureTimestampWindow = 2;
Ken Mixterafcf8082010-10-26 14:45:01 -070033// Kernel log timestamp regular expression.
Ben Chan3c6b82c2014-07-23 14:52:14 -070034const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]";
Ken Mixter03403162010-08-18 15:23:16 -070035
Ben Chan7e776902014-06-18 13:19:51 -070036//
37// These regular expressions enable to us capture the PC in a backtrace.
38// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem
39// feature.
40//
41// For ARM we see:
42// "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4"
Ben Chan120c6752014-07-22 21:06:09 -070043// For MIPS we see:
44// "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8"
Ben Chan7e776902014-06-18 13:19:51 -070045// For x86:
46// "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108
47// SS:ESP 0068:e9dd3efc"
48//
Ben Chan3c6b82c2014-07-23 14:52:14 -070049const char* const kPCRegex[] = {
Simon Glassd74cc092011-04-06 10:47:01 -070050 0,
51 " PC is at ([^\\+ ]+).*",
Ben Chan120c6752014-07-22 21:06:09 -070052 " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*", // MIPS has an exception program counter
Simon Glassd74cc092011-04-06 10:47:01 -070053 " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter
Bryan Freedb8737592012-04-02 17:05:48 -070054 " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter
Simon Glassd74cc092011-04-06 10:47:01 -070055};
56
Ben Chan3c6b82c2014-07-23 14:52:14 -070057COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
Simon Glassd74cc092011-04-06 10:47:01 -070058 missing_arch_pc_regexp);
59
Ben Chan3c6b82c2014-07-23 14:52:14 -070060} // namespace
61
Ken Mixter03403162010-08-18 15:23:16 -070062KernelCollector::KernelCollector()
63 : is_enabled_(false),
Ben Chan2076b902012-02-29 22:26:54 -080064 ramoops_dump_path_(kDumpPath),
Ben Chan3c6b82c2014-07-23 14:52:14 -070065 records_(0),
66 // We expect crash dumps in the format of architecture we are built for.
67 arch_(GetCompilerArch()) {
Ken Mixter03403162010-08-18 15:23:16 -070068}
69
70KernelCollector::~KernelCollector() {
71}
72
73void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) {
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070074 ramoops_dump_path_ = file_path;
75}
76
77bool KernelCollector::ReadRecordToString(std::string *contents,
Kees Cookce9556e2011-11-04 20:49:09 +000078 size_t current_record,
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070079 bool *record_found) {
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070080 // A record is a ramoops dump. It has an associated size of "record_size".
81 std::string record;
82 std::string captured;
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070083
84 // Ramoops appends a header to a crash which contains ==== followed by a
85 // timestamp. Ignore the header.
Ben Chan3c6b82c2014-07-23 14:52:14 -070086 pcrecpp::RE record_re(
87 "====\\d+\\.\\d+\n(.*)",
88 pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070089
Kees Cookce9556e2011-11-04 20:49:09 +000090 FilePath ramoops_record;
91 GetRamoopsRecordPath(&ramoops_record, current_record);
Mike Frysingera557c112014-02-05 22:55:39 -050092 if (!base::ReadFileToString(ramoops_record, &record)) {
Kees Cookce9556e2011-11-04 20:49:09 +000093 LOG(ERROR) << "Unable to open " << ramoops_record.value();
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070094 return false;
95 }
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070096
Ben Chan7e776902014-06-18 13:19:51 -070097 if (record_re.FullMatch(record, &captured)) {
Kees Cook5825d5a2012-02-22 17:15:15 -080098 // Found a match, append it to the content, and remove from pstore.
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -070099 contents->append(captured);
Mike Frysingera557c112014-02-05 22:55:39 -0500100 base::DeleteFile(ramoops_record, false);
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700101 *record_found = true;
102 } else {
103 *record_found = false;
104 }
105
106 return true;
107}
108
Kees Cookce9556e2011-11-04 20:49:09 +0000109void KernelCollector::GetRamoopsRecordPath(FilePath *path,
110 size_t record) {
Michael Krebs1e09a842012-04-18 12:34:13 -0700111 // Disable error "format not a string literal, argument types not checked"
112 // because this is valid, but GNU apparently doesn't bother checking a const
113 // format string.
114 #pragma GCC diagnostic push
115 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
Kees Cookce9556e2011-11-04 20:49:09 +0000116 *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record));
Michael Krebs1e09a842012-04-18 12:34:13 -0700117 #pragma GCC diagnostic pop
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700118}
119
120bool KernelCollector::LoadParameters() {
Kees Cookce9556e2011-11-04 20:49:09 +0000121 // Discover how many ramoops records are being exported by the driver.
122 size_t count;
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700123
Kees Cookce9556e2011-11-04 20:49:09 +0000124 for (count = 0; count < kMaxDumpRecords; ++count) {
125 FilePath ramoops_record;
126 GetRamoopsRecordPath(&ramoops_record, count);
127
Mike Frysingera557c112014-02-05 22:55:39 -0500128 if (!base::PathExists(ramoops_record))
Kees Cookce9556e2011-11-04 20:49:09 +0000129 break;
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700130 }
131
Kees Cookce9556e2011-11-04 20:49:09 +0000132 records_ = count;
133 return (records_ > 0);
Ken Mixter03403162010-08-18 15:23:16 -0700134}
135
136bool KernelCollector::LoadPreservedDump(std::string *contents) {
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700137 // Load dumps from the preserved memory and save them in contents.
138 // Since the system is set to restart on oops we won't actually ever have
139 // multiple records (only 0 or 1), but check in case we don't restart on
140 // oops in the future.
141 bool any_records_found = false;
142 bool record_found = false;
Ken Mixter03403162010-08-18 15:23:16 -0700143 // clear contents since ReadFileToString actually appends to the string.
144 contents->clear();
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700145
Kees Cookce9556e2011-11-04 20:49:09 +0000146 for (size_t i = 0; i < records_; ++i) {
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700147 if (!ReadRecordToString(contents, i, &record_found)) {
148 break;
149 }
150 if (record_found) {
151 any_records_found = true;
152 }
153 }
154
155 if (!any_records_found) {
156 LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value();
Ken Mixter03403162010-08-18 15:23:16 -0700157 return false;
158 }
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700159
Ken Mixter03403162010-08-18 15:23:16 -0700160 return true;
161}
162
Doug Anderson1e6b8bd2011-04-07 09:40:05 -0700163void KernelCollector::StripSensitiveData(std::string *kernel_dump) {
164 // Strip any data that the user might not want sent up to the crash servers.
165 // We'll read in from kernel_dump and also place our output there.
166 //
167 // At the moment, the only sensitive data we strip is MAC addresses.
168
169 // Get rid of things that look like MAC addresses, since they could possibly
170 // give information about where someone has been. This is strings that look
171 // like this: 11:22:33:44:55:66
172 // Complications:
173 // - Within a given kernel_dump, want to be able to tell when the same MAC
174 // was used more than once. Thus, we'll consistently replace the first
175 // MAC found with 00:00:00:00:00:01, the second with ...:02, etc.
176 // - ACPI commands look like MAC addresses. We'll specifically avoid getting
177 // rid of those.
178 std::ostringstream result;
179 std::string pre_mac_str;
180 std::string mac_str;
181 std::map<std::string, std::string> mac_map;
182 pcrecpp::StringPiece input(*kernel_dump);
183
184 // This RE will find the next MAC address and can return us the data preceding
185 // the MAC and the MAC itself.
186 pcrecpp::RE mac_re("(.*?)("
187 "[0-9a-fA-F][0-9a-fA-F]:"
188 "[0-9a-fA-F][0-9a-fA-F]:"
189 "[0-9a-fA-F][0-9a-fA-F]:"
190 "[0-9a-fA-F][0-9a-fA-F]:"
191 "[0-9a-fA-F][0-9a-fA-F]:"
192 "[0-9a-fA-F][0-9a-fA-F])",
193 pcrecpp::RE_Options()
194 .set_multiline(true)
195 .set_dotall(true));
196
197 // This RE will identify when the 'pre_mac_str' shows that the MAC address
198 // was really an ACPI cmd. The full string looks like this:
199 // ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
200 pcrecpp::RE acpi_re("ACPI cmd ef/$",
201 pcrecpp::RE_Options()
202 .set_multiline(true)
203 .set_dotall(true));
204
205 // Keep consuming, building up a result string as we go.
206 while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) {
207 if (acpi_re.PartialMatch(pre_mac_str)) {
208 // We really saw an ACPI command; add to result w/ no stripping.
209 result << pre_mac_str << mac_str;
210 } else {
211 // Found a MAC address; look up in our hash for the mapping.
212 std::string replacement_mac = mac_map[mac_str];
213 if (replacement_mac == "") {
214 // It wasn't present, so build up a replacement string.
215 int mac_id = mac_map.size();
216
217 // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt.
218 replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x",
219 (mac_id & 0xff000000) >> 24,
220 (mac_id & 0x00ff0000) >> 16,
221 (mac_id & 0x0000ff00) >> 8,
222 (mac_id & 0x000000ff));
223 mac_map[mac_str] = replacement_mac;
224 }
225
226 // Dump the string before the MAC and the fake MAC address into result.
227 result << pre_mac_str << replacement_mac;
228 }
229 }
230
231 // One last bit of data might still be in the input.
232 result << input;
233
234 // We'll just assign right back to kernel_dump.
235 *kernel_dump = result.str();
236}
237
Ken Mixter03403162010-08-18 15:23:16 -0700238bool KernelCollector::Enable() {
Ben Chan3c6b82c2014-07-23 14:52:14 -0700239 if (arch_ == kArchUnknown || arch_ >= kArchCount || kPCRegex[arch_] == NULL) {
Simon Glassd74cc092011-04-06 10:47:01 -0700240 LOG(WARNING) << "KernelCollector does not understand this architecture";
241 return false;
Ben Chan3c6b82c2014-07-23 14:52:14 -0700242 }
243
244 FilePath ramoops_record;
245 GetRamoopsRecordPath(&ramoops_record, 0);
246 if (!base::PathExists(ramoops_record)) {
247 LOG(WARNING) << "Kernel does not support crash dumping";
248 return false;
Ken Mixter03403162010-08-18 15:23:16 -0700249 }
250
251 // To enable crashes, we will eventually need to set
252 // the chnv bit in BIOS, but it does not yet work.
Ken Mixtera3249322011-03-03 08:47:38 -0800253 LOG(INFO) << "Enabling kernel crash handling";
Ken Mixter03403162010-08-18 15:23:16 -0700254 is_enabled_ = true;
255 return true;
256}
257
Ken Mixterafcf8082010-10-26 14:45:01 -0700258// Hash a string to a number. We define our own hash function to not
259// be dependent on a C++ library that might change. This function
260// uses basically the same approach as tr1/functional_hash.h but with
261// a larger prime number (16127 vs 131).
262static unsigned HashString(const std::string &input) {
263 unsigned hash = 0;
264 for (size_t i = 0; i < input.length(); ++i)
265 hash = hash * 16127 + input[i];
266 return hash;
267}
268
269void KernelCollector::ProcessStackTrace(
270 pcrecpp::StringPiece kernel_dump,
271 bool print_diagnostics,
272 unsigned *hash,
Luigi Semenzatof6400992011-12-29 13:18:35 -0800273 float *last_stack_timestamp,
274 bool *is_watchdog_crash) {
Ken Mixterafcf8082010-10-26 14:45:01 -0700275 pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
Ben Chan3c6b82c2014-07-23 14:52:14 -0700276 pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) +
Simon Glassd74cc092011-04-06 10:47:01 -0700277 " (Call Trace|Backtrace):$");
278
Luigi Semenzatof6400992011-12-29 13:18:35 -0800279 // Match lines such as the following and grab out "function_name".
280 // The ? may or may not be present.
281 //
Simon Glassd74cc092011-04-06 10:47:01 -0700282 // For ARM:
Luigi Semenzatof6400992011-12-29 13:18:35 -0800283 // <4>[ 3498.731164] [<c0057220>] ? (function_name+0x20/0x2c) from
284 // [<c018062c>] (foo_bar+0xdc/0x1bc)
Simon Glassd74cc092011-04-06 10:47:01 -0700285 //
Ben Chan120c6752014-07-22 21:06:09 -0700286 // For MIPS:
287 // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8
288 //
Simon Glassd74cc092011-04-06 10:47:01 -0700289 // For X86:
Luigi Semenzatof6400992011-12-29 13:18:35 -0800290 // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c
291 //
Ben Chan3c6b82c2014-07-23 14:52:14 -0700292 pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) +
Simon Glassd74cc092011-04-06 10:47:01 -0700293 "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]"
294 "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86)
295 "([^\\+ )]+)"); // Matches until delimiter reached
Ken Mixterafcf8082010-10-26 14:45:01 -0700296 std::string line;
297 std::string hashable;
Luigi Semenzatof6400992011-12-29 13:18:35 -0800298 std::string previous_hashable;
299 bool is_watchdog = false;
Ken Mixterafcf8082010-10-26 14:45:01 -0700300
301 *hash = 0;
302 *last_stack_timestamp = 0;
303
Luigi Semenzatof6400992011-12-29 13:18:35 -0800304 // Find the last and second-to-last stack traces. The latter is used when
305 // the panic is from a watchdog timeout.
Ken Mixterafcf8082010-10-26 14:45:01 -0700306 while (line_re.FindAndConsume(&kernel_dump, &line)) {
307 std::string certainty;
308 std::string function_name;
309 if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
310 if (print_diagnostics) {
Luigi Semenzatof6400992011-12-29 13:18:35 -0800311 printf("Stack trace starting.%s\n",
312 hashable.empty() ? "" : " Saving prior trace.");
Ken Mixterafcf8082010-10-26 14:45:01 -0700313 }
Luigi Semenzatof6400992011-12-29 13:18:35 -0800314 previous_hashable = hashable;
Ken Mixterafcf8082010-10-26 14:45:01 -0700315 hashable.clear();
Luigi Semenzatof6400992011-12-29 13:18:35 -0800316 is_watchdog = false;
Ken Mixterafcf8082010-10-26 14:45:01 -0700317 } else if (stack_entry_re.PartialMatch(line,
318 last_stack_timestamp,
319 &certainty,
320 &function_name)) {
321 bool is_certain = certainty.find('?') == std::string::npos;
322 if (print_diagnostics) {
323 printf("@%f: stack entry for %s (%s)\n",
324 *last_stack_timestamp,
325 function_name.c_str(),
326 is_certain ? "certain" : "uncertain");
327 }
328 // Do not include any uncertain (prefixed by '?') frames in our hash.
329 if (!is_certain)
330 continue;
331 if (!hashable.empty())
332 hashable.append("|");
Luigi Semenzatof6400992011-12-29 13:18:35 -0800333 if (function_name == "watchdog_timer_fn" ||
334 function_name == "watchdog") {
335 is_watchdog = true;
336 }
Ken Mixterafcf8082010-10-26 14:45:01 -0700337 hashable.append(function_name);
338 }
339 }
340
Luigi Semenzatof6400992011-12-29 13:18:35 -0800341 // If the last stack trace contains a watchdog function we assume the panic
342 // is from the watchdog timer, and we hash the previous stack trace rather
343 // than the last one, assuming that the previous stack is that of the hung
344 // thread.
345 //
346 // In addition, if the hashable is empty (meaning all frames are uncertain,
347 // for whatever reason) also use the previous frame, as it cannot be any
348 // worse.
349 if (is_watchdog || hashable.empty()) {
350 hashable = previous_hashable;
351 }
352
Ken Mixterafcf8082010-10-26 14:45:01 -0700353 *hash = HashString(hashable);
Luigi Semenzatof6400992011-12-29 13:18:35 -0800354 *is_watchdog_crash = is_watchdog;
Ken Mixterafcf8082010-10-26 14:45:01 -0700355
356 if (print_diagnostics) {
357 printf("Hash based on stack trace: \"%s\" at %f.\n",
358 hashable.c_str(), *last_stack_timestamp);
359 }
360}
361
Ben Chan3c6b82c2014-07-23 14:52:14 -0700362// static
363KernelCollector::ArchKind KernelCollector::GetCompilerArch() {
Simon Glassd74cc092011-04-06 10:47:01 -0700364#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
Ben Chan3c6b82c2014-07-23 14:52:14 -0700365 return kArchArm;
Ben Chan120c6752014-07-22 21:06:09 -0700366#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
Ben Chan3c6b82c2014-07-23 14:52:14 -0700367 return kArchMips;
Bryan Freedb8737592012-04-02 17:05:48 -0700368#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64)
Ben Chan3c6b82c2014-07-23 14:52:14 -0700369 return kArchX86_64;
Simon Glassd74cc092011-04-06 10:47:01 -0700370#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
Ben Chan3c6b82c2014-07-23 14:52:14 -0700371 return kArchX86;
Simon Glassd74cc092011-04-06 10:47:01 -0700372#else
Ben Chan3c6b82c2014-07-23 14:52:14 -0700373 return kArchUnknown;
Simon Glassd74cc092011-04-06 10:47:01 -0700374#endif
375}
376
Ken Mixterafcf8082010-10-26 14:45:01 -0700377bool KernelCollector::FindCrashingFunction(
Simon Glassd74cc092011-04-06 10:47:01 -0700378 pcrecpp::StringPiece kernel_dump,
379 bool print_diagnostics,
380 float stack_trace_timestamp,
381 std::string *crashing_function) {
Ken Mixterafcf8082010-10-26 14:45:01 -0700382 float timestamp = 0;
Simon Glassd74cc092011-04-06 10:47:01 -0700383
384 // Use the correct regex for this architecture.
Ben Chan3c6b82c2014-07-23 14:52:14 -0700385 pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_],
Simon Glassd74cc092011-04-06 10:47:01 -0700386 pcrecpp::MULTILINE());
387
Ken Mixterafcf8082010-10-26 14:45:01 -0700388 while (eip_re.FindAndConsume(&kernel_dump, &timestamp, crashing_function)) {
389 if (print_diagnostics) {
390 printf("@%f: found crashing function %s\n",
391 timestamp,
392 crashing_function->c_str());
393 }
394 }
395 if (timestamp == 0) {
396 if (print_diagnostics) {
397 printf("Found no crashing function.\n");
398 }
399 return false;
400 }
401 if (stack_trace_timestamp != 0 &&
Yunlian Jiang9f520402014-02-27 14:26:15 -0800402 abs(static_cast<int>(stack_trace_timestamp - timestamp))
403 > kSignatureTimestampWindow) {
Ken Mixterafcf8082010-10-26 14:45:01 -0700404 if (print_diagnostics) {
405 printf("Found crashing function but not within window.\n");
406 }
407 return false;
408 }
409 if (print_diagnostics) {
410 printf("Found crashing function %s\n", crashing_function->c_str());
411 }
412 return true;
413}
414
415bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
416 bool print_diagnostics,
417 std::string *panic_message) {
418 // Match lines such as the following and grab out "Fatal exception"
419 // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception
Ben Chan3c6b82c2014-07-23 14:52:14 -0700420 pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) +
Ken Mixterafcf8082010-10-26 14:45:01 -0700421 " Kernel panic[^\\:]*\\:\\s*(.*)",
422 pcrecpp::MULTILINE());
423 float timestamp = 0;
424 while (kernel_panic_re.FindAndConsume(&kernel_dump,
425 &timestamp,
426 panic_message)) {
427 if (print_diagnostics) {
428 printf("@%f: panic message %s\n",
429 timestamp,
430 panic_message->c_str());
431 }
432 }
433 if (timestamp == 0) {
434 if (print_diagnostics) {
435 printf("Found no panic message.\n");
436 }
437 return false;
438 }
439 return true;
440}
441
442bool KernelCollector::ComputeKernelStackSignature(
443 const std::string &kernel_dump,
444 std::string *kernel_signature,
445 bool print_diagnostics) {
446 unsigned stack_hash = 0;
447 float last_stack_timestamp = 0;
448 std::string human_string;
Luigi Semenzatof6400992011-12-29 13:18:35 -0800449 bool is_watchdog_crash;
Ken Mixterafcf8082010-10-26 14:45:01 -0700450
451 ProcessStackTrace(kernel_dump,
452 print_diagnostics,
453 &stack_hash,
Luigi Semenzatof6400992011-12-29 13:18:35 -0800454 &last_stack_timestamp,
455 &is_watchdog_crash);
Ken Mixterafcf8082010-10-26 14:45:01 -0700456
457 if (!FindCrashingFunction(kernel_dump,
458 print_diagnostics,
459 last_stack_timestamp,
460 &human_string)) {
461 if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
462 if (print_diagnostics) {
463 printf("Found no human readable string, using empty string.\n");
464 }
465 human_string.clear();
466 }
467 }
468
469 if (human_string.empty() && stack_hash == 0) {
470 if (print_diagnostics) {
471 printf("Found neither a stack nor a human readable string, failing.\n");
472 }
473 return false;
474 }
475
476 human_string = human_string.substr(0, kMaxHumanStringLength);
Luigi Semenzatof6400992011-12-29 13:18:35 -0800477 *kernel_signature = StringPrintf("%s-%s%s-%08X",
Ken Mixterafcf8082010-10-26 14:45:01 -0700478 kKernelExecName,
Luigi Semenzatof6400992011-12-29 13:18:35 -0800479 (is_watchdog_crash ? "(HANG)-" : ""),
Ken Mixterafcf8082010-10-26 14:45:01 -0700480 human_string.c_str(),
481 stack_hash);
482 return true;
483}
484
Ken Mixter03403162010-08-18 15:23:16 -0700485bool KernelCollector::Collect() {
486 std::string kernel_dump;
487 FilePath root_crash_directory;
Sergiu Iordache1ea8abe2011-08-03 16:11:36 -0700488
489 if (!LoadParameters()) {
490 return false;
491 }
Ken Mixter03403162010-08-18 15:23:16 -0700492 if (!LoadPreservedDump(&kernel_dump)) {
493 return false;
494 }
Doug Anderson1e6b8bd2011-04-07 09:40:05 -0700495 StripSensitiveData(&kernel_dump);
Ken Mixter03403162010-08-18 15:23:16 -0700496 if (kernel_dump.empty()) {
497 return false;
498 }
Ken Mixterafcf8082010-10-26 14:45:01 -0700499 std::string signature;
500 if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
501 signature = kDefaultKernelStackSignature;
502 }
Ken Mixteree849c52010-09-30 15:30:10 -0700503
Ken Mixter9ee1f5f2011-10-25 02:15:05 +0000504 std::string reason = "handling";
505 bool feedback = true;
506 if (IsDeveloperImage()) {
507 reason = "developer build - always dumping";
508 feedback = true;
509 } else if (!is_feedback_allowed_function_()) {
510 reason = "ignoring - no consent";
511 feedback = false;
512 }
Ken Mixterafcf8082010-10-26 14:45:01 -0700513
Ken Mixtera3249322011-03-03 08:47:38 -0800514 LOG(INFO) << "Received prior crash notification from "
Ken Mixter9ee1f5f2011-10-25 02:15:05 +0000515 << "kernel (signature " << signature << ") (" << reason << ")";
Ken Mixterafcf8082010-10-26 14:45:01 -0700516
517 if (feedback) {
Ken Mixter03403162010-08-18 15:23:16 -0700518 count_crash_function_();
519
520 if (!GetCreatedCrashDirectoryByEuid(kRootUid,
Ken Mixter207694d2010-10-28 15:42:37 -0700521 &root_crash_directory,
522 NULL)) {
Ken Mixter03403162010-08-18 15:23:16 -0700523 return true;
524 }
525
Ken Mixteree849c52010-09-30 15:30:10 -0700526 std::string dump_basename =
Ben Chan3c6b82c2014-07-23 14:52:14 -0700527 FormatDumpBasename(kKernelExecName, time(NULL), kKernelPid);
Ken Mixteree849c52010-09-30 15:30:10 -0700528 FilePath kernel_crash_path = root_crash_directory.Append(
529 StringPrintf("%s.kcrash", dump_basename.c_str()));
530
Ben Chanf30c6412014-05-22 23:09:01 -0700531 // We must use WriteNewFile instead of base::WriteFile as we
Ken Mixter9b346472010-11-07 13:45:45 -0800532 // do not want to write with root access to a symlink that an attacker
533 // might have created.
534 if (WriteNewFile(kernel_crash_path,
535 kernel_dump.data(),
536 kernel_dump.length()) !=
Ken Mixter03403162010-08-18 15:23:16 -0700537 static_cast<int>(kernel_dump.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -0800538 LOG(INFO) << "Failed to write kernel dump to "
539 << kernel_crash_path.value().c_str();
Ken Mixter03403162010-08-18 15:23:16 -0700540 return true;
541 }
542
Ken Mixterafcf8082010-10-26 14:45:01 -0700543 AddCrashMetaData(kKernelSignatureKey, signature);
Ken Mixteree849c52010-09-30 15:30:10 -0700544 WriteCrashMetaData(
545 root_crash_directory.Append(
546 StringPrintf("%s.meta", dump_basename.c_str())),
Ken Mixterc909b692010-10-18 12:26:05 -0700547 kKernelExecName,
548 kernel_crash_path.value());
Ken Mixteree849c52010-09-30 15:30:10 -0700549
Ken Mixtera3249322011-03-03 08:47:38 -0800550 LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value();
Ken Mixter03403162010-08-18 15:23:16 -0700551 }
Ken Mixter03403162010-08-18 15:23:16 -0700552
553 return true;
554}