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
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
index 652e7ff..56fa301 100644
--- a/crash_reporter/kernel_collector.cc
+++ b/crash_reporter/kernel_collector.cc
@@ -24,9 +24,31 @@
 // 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::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 @@
     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] [<c0057220>] (__bug+0x20/0x2c) from [<c018062c>]
+  // (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 @@
   }
 }
 
+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, &timestamp, 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 708fd1b..04f3bdd 100644
--- a/crash_reporter/kernel_collector.h
+++ b/crash_reporter/kernel_collector.h
@@ -18,6 +18,15 @@
 // 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 @@
                                    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 @@
                         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 f7c88f0..08e3169 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -50,11 +50,17 @@
 
   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 @@
   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 : [<c0058220>]    lr : [<c005821c>]    "
+          "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] [<c0058220>] (__bug+0x20/0x2c) from [<c0183678>] "
+          "(write_breakme+0xdc/0x1bc)\n"
+      "<5>[  123.412798] [<c0183678>] (write_breakme+0xdc/0x1bc) from "
+          "[<c017bfe0>] (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 @@
       "<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 @@
       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 @@
                                              &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) {