From d74cc09bbd6829535e1ce0b29bf2ef7de9fd8468 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 6 Apr 2011 10:47:01 -0700 Subject: [PATCH] Add ARM support Adds support for scanning ARM backtraces so that kernel crash reporting can work on ARM. BUG=chromium-os:12454 TEST=cat /sys/kernel/debug/preserved/kcrash > x crash_reporter -generate_kernel_signature x Verify that it finds a backtrace properly. FEATURES=test emerge-x86-mario crash-reporter - check all tests pass (this will run both ARM and X86 tests) Change-Id: I4dc6d7a2bae53d05883da9425ec8e9ac4a5c2bba Review URL: http://codereview.chromium.org/6599022 --- crash_reporter/kernel_collector.cc | 76 +++++++++++++-- crash_reporter/kernel_collector.h | 19 ++++ crash_reporter/kernel_collector_test.cc | 121 +++++++++++++++++++----- 3 files changed, 180 insertions(+), 36 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 652e7ff3a..56fa301f7 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -24,9 +24,31 @@ static const int kSignatureTimestampWindow = 2; // Kernel log timestamp regular expression. static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); +/* + * These regular expressions enable to us capture the PC in a backtrace. + * The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem + * feature. + * + * For ARM we see: + * "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" + * For x86: + * "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 \ + * SS:ESP 0068:e9dd3efc + */ +static const char *s_pc_regex[] = { + 0, + " PC is at ([^\\+ ]+).*", + " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter +}; + +COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, + missing_arch_pc_regexp); + KernelCollector::KernelCollector() : is_enabled_(false), preserved_dump_path_(kPreservedDumpPath) { + // We expect crash dumps in the format of the architecture we are built for. + arch_ = GetCompilerArch(); } KernelCollector::~KernelCollector() { @@ -47,7 +69,12 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { } bool KernelCollector::Enable() { - if (!file_util::PathExists(preserved_dump_path_)) { + if (arch_ == archUnknown || arch_ >= archCount || + s_pc_regex[arch_] == NULL) { + LOG(WARNING) << "KernelCollector does not understand this architecture"; + return false; + } + else if (!file_util::PathExists(preserved_dump_path_)) { LOG(WARNING) << "Kernel does not support crash dumping"; return false; } @@ -90,11 +117,21 @@ void KernelCollector::ProcessStackTrace( unsigned *hash, float *last_stack_timestamp) { pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); - pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$"); + pcrecpp::RE stack_trace_start_re(kTimestampRegex + + " (Call Trace|Backtrace):$"); + + // For ARM: + // <4>[ 3498.731164] [] (__bug+0x20/0x2c) from [] + // (write_breakme+0xdc/0x1bc) + // + // For X86: // Match lines such as the following and grab out "error_code". - // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c + // <4>[ 6066.849504] [<7937bcee>] ? error_code+0x66/0x6c + // The ? may or may not be present pcrecpp::RE stack_entry_re(kTimestampRegex + - " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)"); + "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]" + "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86) + "([^\\+ )]+)"); // Matches until delimiter reached std::string line; std::string hashable; @@ -137,14 +174,33 @@ void KernelCollector::ProcessStackTrace( } } +enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) +{ +#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) + return archArm; +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) + return archX86; +#else + return archUnknown; +#endif +} + +void KernelCollector::SetArch(enum ArchKind arch) +{ + arch_ = arch; +} + 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()); + pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + float stack_trace_timestamp, + std::string *crashing_function) { float timestamp = 0; + + // Use the correct regex for this architecture. + pcrecpp::RE eip_re(kTimestampRegex + s_pc_regex[arch_], + pcrecpp::MULTILINE()); + while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { if (print_diagnostics) { printf("@%f: found crashing function %s\n", diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 708fd1b9e..04f3bdd65 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -18,6 +18,15 @@ class FilePath; // Kernel crash collector. class KernelCollector : public CrashCollector { public: + // Enumeration to specify architecture type. + enum ArchKind { + archUnknown, + archArm, + archX86, + + archCount // Number of architectures. + }; + KernelCollector(); virtual ~KernelCollector(); @@ -41,6 +50,10 @@ class KernelCollector : public CrashCollector { std::string *kernel_signature, bool print_diagnostics); + // Set the architecture of the crash dumps we are looking at. + void SetArch(enum ArchKind arch); + enum ArchKind GetArch() { return arch_; } + private: friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); @@ -62,9 +75,15 @@ class KernelCollector : public CrashCollector { bool print_diagnostics, std::string *panic_message); + // Returns the architecture kind for which we are built - enum ArchKind. + enum ArchKind GetCompilerArch(void); + bool is_enabled_; FilePath preserved_dump_path_; static const char kClearingSequence[]; + + // The architecture of kernel dump strings we are working with. + enum ArchKind arch_; }; #endif // _CRASH_REPORTER_KERNEL_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index f7c88f09a..08e31698b 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -50,11 +50,17 @@ class KernelCollectorTest : public ::testing::Test { void SetUpSuccessfulCollect(); void CheckPreservedDumpClear(); + void ComputeKernelStackSignatureCommon(); KernelCollector collector_; FilePath test_kcrash_; }; +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { + // Make sure the normal build architecture is detected + EXPECT_TRUE(collector_.GetArch() != KernelCollector::archUnknown); +} + TEST_F(KernelCollectorTest, LoadPreservedDump) { ASSERT_FALSE(file_util::PathExists(test_kcrash_)); std::string dump; @@ -162,7 +168,92 @@ TEST_F(KernelCollectorTest, CollectOK) { CheckPreservedDumpClear(); } -TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { +// Perform tests which are common across architectures +void KernelCollectorTest::ComputeKernelStackSignatureCommon() { + std::string signature; + + const char kStackButNoPC[] = + "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false)); + EXPECT_EQ("kernel--83615F0A", signature); + + const char kMissingEverything[] = + "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n"; + EXPECT_FALSE( + collector_.ComputeKernelStackSignature(kMissingEverything, + &signature, + false)); + + // Long message. + const char kTruncatedMessage[] = + "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789" + "01234567890123456789X\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kTruncatedMessage, + &signature, + false)); + EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000", + signature); +} + +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { + const char kBugToPanic[] = + "<5>[ 123.412524] Modules linked in:\n" + "<5>[ 123.412534] CPU: 0 Tainted: G W " + "(2.6.37-01030-g51cee64 #153)\n" + "<5>[ 123.412552] PC is at write_breakme+0xd0/0x1b4\n" + "<5>[ 123.412560] LR is at write_breakme+0xc8/0x1b4\n" + "<5>[ 123.412569] pc : [] lr : [] " + "psr: 60000013\n" + "<5>[ 123.412574] sp : f4e0ded8 ip : c04d104c fp : 000e45e0\n" + "<5>[ 123.412581] r10: 400ff000 r9 : f4e0c000 r8 : 00000004\n" + "<5>[ 123.412589] r7 : f4e0df80 r6 : f4820c80 r5 : 00000004 " + "r4 : f4e0dee8\n" + "<5>[ 123.412598] r3 : 00000000 r2 : f4e0decc r1 : c05f88a9 " + "r0 : 00000039\n" + "<5>[ 123.412608] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA " + "ARM Segment user\n" + "<5>[ 123.412617] Control: 10c53c7d Table: 34dcc04a DAC: 00000015\n" + "<0>[ 123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n" + "<0>[ 123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n" + "<0>[ 123.412641] dec0: " + " f4e0dee8 c0183678\n" + "<0>[ 123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 " + "400ff000 f4e0dfb0 00000000\n" + "<0>[ 123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 " + "c024acc8 00000001 c018359c\n" + "<0>[ 123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 " + "f5803c80 c018359c c017bfe0\n" + "<0>[ 123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 " + "f4e0c000 00000000 c01383e4\n" + "<0>[ 123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 " + "00000000 00000004 c0138578\n" + "<0>[ 123.412715] df80: 00000000 00000000 00000004 00000000 00000004 " + "402f95d0 00000004 00000004\n" + "<0>[ 123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 " + "400ff000 00000004 00000000\n" + "<0>[ 123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 " + "000c194c bec7ab58 000e45e0\n" + "<0>[ 123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 " + "00000001 00000000 00000000\n" + "<5>[ 39.496577] Backtrace:\n" + "<5>[ 123.412782] [] (__bug+0x20/0x2c) from [] " + "(write_breakme+0xdc/0x1bc)\n" + "<5>[ 123.412798] [] (write_breakme+0xdc/0x1bc) from " + "[] (proc_reg_write+0x88/0x9c)\n"; + std::string signature; + + collector_.SetArch(KernelCollector::archArm); + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); + EXPECT_EQ("kernel-write_breakme-97D3E92F", signature); + + ComputeKernelStackSignatureCommon(); +} + + +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { const char kBugToPanic[] = "<4>[ 6066.829029] [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n" "<4>[ 6066.829029] [<790340af>] ignore_old_stack+0xa6/0x143\n" @@ -178,6 +269,8 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { "<4>[ 6066.949971] [<7937c5c5>] oops_end+0x73/0x81\n" "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n"; std::string signature; + + collector_.SetArch(KernelCollector::archX86); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature); @@ -188,19 +281,6 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false)); EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature); - const char kStackButNoPC[] = - "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n"; - EXPECT_TRUE( - collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false)); - EXPECT_EQ("kernel--83615F0A", signature); - - const char kMissingEverything[] = - "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n"; - EXPECT_FALSE( - collector_.ComputeKernelStackSignature(kMissingEverything, - &signature, - false)); - const char kBreakmeBug[] = "<4>[ 180.492137] [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n" "<4>[ 180.492137] [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n" @@ -255,18 +335,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { &signature, false)); EXPECT_EQ("kernel-Testing panic-E0FC3552", signature); - - // Long message. - const char kTruncatedMessage[] = - "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789" - "01234567890123456789X\n"; - EXPECT_TRUE( - collector_.ComputeKernelStackSignature(kTruncatedMessage, - &signature, - false)); - EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000", - signature); - + ComputeKernelStackSignatureCommon(); } int main(int argc, char **argv) {