crash-reporter: Capture and send recent update_engine logs when it crashes

We also disallow more than 4 nested crashes.  This way we know if core2md crashes for instance, but not if crash_reporter crashes while handling the core2md crash that came from crash_reporter.  Note that the kernel does not know about parent/child process relationships, it just counts concurrent handling, so it is possible that some of many crashing processes will be discarded.

See crash report id 473c22ed428d33a9 for an example report with update_engine logs.

Change-Id: I9ff9f03a94dc87d2d42840511c2e5e42ee37cea8

BUG=9176
TEST=UserCrash,CrashSender,unit tests

Review URL: http://codereview.chromium.org/5814001
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index bf3a9dd..7f1d4c2 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -271,6 +271,15 @@
   EXPECT_FALSE(CheckHasCapacity());
 }
 
+TEST_F(CrashCollectorTest, IsCommentLine) {
+  EXPECT_FALSE(CrashCollector::IsCommentLine(""));
+  EXPECT_TRUE(CrashCollector::IsCommentLine("#"));
+  EXPECT_TRUE(CrashCollector::IsCommentLine("#real comment"));
+  EXPECT_TRUE(CrashCollector::IsCommentLine(" # real comment"));
+  EXPECT_FALSE(CrashCollector::IsCommentLine("not comment"));
+  EXPECT_FALSE(CrashCollector::IsCommentLine(" not comment"));
+}
+
 TEST_F(CrashCollectorTest, ReadKeyValueFile) {
   const char *contents = ("a=b\n"
                           "\n"
@@ -294,10 +303,13 @@
               " f g = h\n"
               "i=j\n"
               "=k\n"
+              "#comment=0\n"
               "l=\n");
   file_util::WriteFile(path, contents, strlen(contents));
 
   EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary));
+  EXPECT_EQ(5, dictionary.size());
+
   i = dictionary.find("a");
   EXPECT_TRUE(i != dictionary.end() && i->second == "b c d");
   i = dictionary.find("e");
@@ -369,6 +381,27 @@
   EXPECT_NE(std::string::npos, logging_.log().find("Unable to write"));
 }
 
+TEST_F(CrashCollectorTest, GetLogContents) {
+  FilePath config_file = test_dir_.Append("crash_config");
+  FilePath output_file = test_dir_.Append("crash_log");
+  const char kConfigContents[] =
+      "foobar:echo hello there | sed -e \"s/there/world/\"";
+  ASSERT_TRUE(
+      file_util::WriteFile(config_file,
+                           kConfigContents, strlen(kConfigContents)));
+  EXPECT_FALSE(collector_.GetLogContents(config_file,
+                                         "barfoo",
+                                         output_file));
+  EXPECT_FALSE(file_util::PathExists(output_file));
+  EXPECT_TRUE(collector_.GetLogContents(config_file,
+                                        "foobar",
+                                        output_file));
+  ASSERT_TRUE(file_util::PathExists(output_file));
+  std::string contents;
+  EXPECT_TRUE(file_util::ReadFileToString(output_file, &contents));
+  EXPECT_EQ("hello world\n", contents);
+}
+
 int main(int argc, char **argv) {
   ::testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();