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 | 10 |
11 const char KernelCollector::kClearingSequence[] = " "; | 11 const char KernelCollector::kClearingSequence[] = " "; |
12 static const char kDefaultKernelStackSignature[] = | 12 static const char kDefaultKernelStackSignature[] = |
13 "kernel-UnspecifiedStackSignature"; | 13 "kernel-UnspecifiedStackSignature"; |
14 static const char kKernelExecName[] = "kernel"; | 14 static const char kKernelExecName[] = "kernel"; |
15 const pid_t kKernelPid = 0; | 15 const pid_t kKernelPid = 0; |
16 static const char kKernelSignatureKey[] = "sig"; | 16 static const char kKernelSignatureKey[] = "sig"; |
17 // Byte length of maximum human readable portion of a kernel crash signature. | 17 // Byte length of maximum human readable portion of a kernel crash signature. |
18 static const int kMaxHumanStringLength = 40; | 18 static const int kMaxHumanStringLength = 40; |
19 static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; | 19 static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; |
20 const uid_t kRootUid = 0; | 20 const uid_t kRootUid = 0; |
21 // Time in seconds from the final kernel log message for a call stack | 21 // Time in seconds from the final kernel log message for a call stack |
22 // to count towards the signature of the kcrash. | 22 // to count towards the signature of the kcrash. |
23 static const int kSignatureTimestampWindow = 2; | 23 static const int kSignatureTimestampWindow = 2; |
24 // Kernel log timestamp regular expression. | 24 // Kernel log timestamp regular expression. |
25 static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); | 25 static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); |
26 | 26 |
| 27 /* |
| 28 * These regular expressions enable to us capture the PC in a backtrace. |
| 29 * The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem |
| 30 * feature. |
| 31 * |
| 32 * For ARM we see: |
| 33 * "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" |
| 34 * For x86: |
| 35 * "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 \ |
| 36 * SS:ESP 0068:e9dd3efc |
| 37 */ |
| 38 static const char *s_pc_regex[] = { |
| 39 0, |
| 40 " PC is at ([^\\+ ]+).*", |
| 41 " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter |
| 42 }; |
| 43 |
| 44 COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, |
| 45 missing_arch_pc_regexp); |
| 46 |
27 KernelCollector::KernelCollector() | 47 KernelCollector::KernelCollector() |
28 : is_enabled_(false), | 48 : is_enabled_(false), |
29 preserved_dump_path_(kPreservedDumpPath) { | 49 preserved_dump_path_(kPreservedDumpPath) { |
| 50 // We expect crash dumps in the format of the architecture we are built for. |
| 51 arch_ = GetCompilerArch(); |
30 } | 52 } |
31 | 53 |
32 KernelCollector::~KernelCollector() { | 54 KernelCollector::~KernelCollector() { |
33 } | 55 } |
34 | 56 |
35 void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { | 57 void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { |
36 preserved_dump_path_ = file_path; | 58 preserved_dump_path_ = file_path; |
37 } | 59 } |
38 | 60 |
39 bool KernelCollector::LoadPreservedDump(std::string *contents) { | 61 bool KernelCollector::LoadPreservedDump(std::string *contents) { |
40 // clear contents since ReadFileToString actually appends to the string. | 62 // clear contents since ReadFileToString actually appends to the string. |
41 contents->clear(); | 63 contents->clear(); |
42 if (!file_util::ReadFileToString(preserved_dump_path_, contents)) { | 64 if (!file_util::ReadFileToString(preserved_dump_path_, contents)) { |
43 LOG(ERROR) << "Unable to read " << preserved_dump_path_.value(); | 65 LOG(ERROR) << "Unable to read " << preserved_dump_path_.value(); |
44 return false; | 66 return false; |
45 } | 67 } |
46 return true; | 68 return true; |
47 } | 69 } |
48 | 70 |
49 bool KernelCollector::Enable() { | 71 bool KernelCollector::Enable() { |
50 if (!file_util::PathExists(preserved_dump_path_)) { | 72 if (arch_ == archUnknown || arch_ >= archCount || |
| 73 s_pc_regex[arch_] == NULL) { |
| 74 LOG(WARNING) << "KernelCollector does not understand this architecture"; |
| 75 return false; |
| 76 } |
| 77 else if (!file_util::PathExists(preserved_dump_path_)) { |
51 LOG(WARNING) << "Kernel does not support crash dumping"; | 78 LOG(WARNING) << "Kernel does not support crash dumping"; |
52 return false; | 79 return false; |
53 } | 80 } |
54 | 81 |
55 // To enable crashes, we will eventually need to set | 82 // To enable crashes, we will eventually need to set |
56 // the chnv bit in BIOS, but it does not yet work. | 83 // the chnv bit in BIOS, but it does not yet work. |
57 LOG(INFO) << "Enabling kernel crash handling"; | 84 LOG(INFO) << "Enabling kernel crash handling"; |
58 is_enabled_ = true; | 85 is_enabled_ = true; |
59 return true; | 86 return true; |
60 } | 87 } |
(...skipping 22 matching lines...) Expand all Loading... |
83 hash = hash * 16127 + input[i]; | 110 hash = hash * 16127 + input[i]; |
84 return hash; | 111 return hash; |
85 } | 112 } |
86 | 113 |
87 void KernelCollector::ProcessStackTrace( | 114 void KernelCollector::ProcessStackTrace( |
88 pcrecpp::StringPiece kernel_dump, | 115 pcrecpp::StringPiece kernel_dump, |
89 bool print_diagnostics, | 116 bool print_diagnostics, |
90 unsigned *hash, | 117 unsigned *hash, |
91 float *last_stack_timestamp) { | 118 float *last_stack_timestamp) { |
92 pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); | 119 pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); |
93 pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$"); | 120 pcrecpp::RE stack_trace_start_re(kTimestampRegex + |
| 121 " (Call Trace|Backtrace):$"); |
| 122 |
| 123 // For ARM: |
| 124 // <4>[ 3498.731164] [<c0057220>] (__bug+0x20/0x2c) from [<c018062c>] |
| 125 // (write_breakme+0xdc/0x1bc) |
| 126 // |
| 127 // For X86: |
94 // Match lines such as the following and grab out "error_code". | 128 // Match lines such as the following and grab out "error_code". |
95 // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c | 129 // <4>[ 6066.849504] [<7937bcee>] ? error_code+0x66/0x6c |
| 130 // The ? may or may not be present |
96 pcrecpp::RE stack_entry_re(kTimestampRegex + | 131 pcrecpp::RE stack_entry_re(kTimestampRegex + |
97 " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)"); | 132 "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]" |
| 133 "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86) |
| 134 "([^\\+ )]+)"); // Matches until delimiter reached |
98 std::string line; | 135 std::string line; |
99 std::string hashable; | 136 std::string hashable; |
100 | 137 |
101 *hash = 0; | 138 *hash = 0; |
102 *last_stack_timestamp = 0; | 139 *last_stack_timestamp = 0; |
103 | 140 |
104 while (line_re.FindAndConsume(&kernel_dump, &line)) { | 141 while (line_re.FindAndConsume(&kernel_dump, &line)) { |
105 std::string certainty; | 142 std::string certainty; |
106 std::string function_name; | 143 std::string function_name; |
107 if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) { | 144 if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) { |
(...skipping 22 matching lines...) Expand all Loading... |
130 } | 167 } |
131 | 168 |
132 *hash = HashString(hashable); | 169 *hash = HashString(hashable); |
133 | 170 |
134 if (print_diagnostics) { | 171 if (print_diagnostics) { |
135 printf("Hash based on stack trace: \"%s\" at %f.\n", | 172 printf("Hash based on stack trace: \"%s\" at %f.\n", |
136 hashable.c_str(), *last_stack_timestamp); | 173 hashable.c_str(), *last_stack_timestamp); |
137 } | 174 } |
138 } | 175 } |
139 | 176 |
| 177 enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) |
| 178 { |
| 179 #if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) |
| 180 return archArm; |
| 181 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) |
| 182 return archX86; |
| 183 #else |
| 184 return archUnknown; |
| 185 #endif |
| 186 } |
| 187 |
| 188 void KernelCollector::SetArch(enum ArchKind arch) |
| 189 { |
| 190 arch_ = arch; |
| 191 } |
| 192 |
140 bool KernelCollector::FindCrashingFunction( | 193 bool KernelCollector::FindCrashingFunction( |
141 pcrecpp::StringPiece kernel_dump, | 194 pcrecpp::StringPiece kernel_dump, |
142 bool print_diagnostics, | 195 bool print_diagnostics, |
143 float stack_trace_timestamp, | 196 float stack_trace_timestamp, |
144 std::string *crashing_function) { | 197 std::string *crashing_function) { |
145 pcrecpp::RE eip_re(kTimestampRegex + " EIP: \\[<.*>\\] ([^\\+ ]+).*", | 198 float timestamp = 0; |
| 199 |
| 200 // Use the correct regex for this architecture. |
| 201 pcrecpp::RE eip_re(kTimestampRegex + s_pc_regex[arch_], |
146 pcrecpp::MULTILINE()); | 202 pcrecpp::MULTILINE()); |
147 float timestamp = 0; | 203 |
148 while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { | 204 while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { |
149 if (print_diagnostics) { | 205 if (print_diagnostics) { |
150 printf("@%f: found crashing function %s\n", | 206 printf("@%f: found crashing function %s\n", |
151 timestamp, | 207 timestamp, |
152 crashing_function->c_str()); | 208 crashing_function->c_str()); |
153 } | 209 } |
154 } | 210 } |
155 if (timestamp == 0) { | 211 if (timestamp == 0) { |
156 if (print_diagnostics) { | 212 if (print_diagnostics) { |
157 printf("Found no crashing function.\n"); | 213 printf("Found no crashing function.\n"); |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 kernel_crash_path.value()); | 350 kernel_crash_path.value()); |
295 | 351 |
296 LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value(); | 352 LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value(); |
297 } | 353 } |
298 if (!ClearPreservedDump()) { | 354 if (!ClearPreservedDump()) { |
299 return false; | 355 return false; |
300 } | 356 } |
301 | 357 |
302 return true; | 358 return true; |
303 } | 359 } |
OLD | NEW |