shill: Implement a basic DiagnosticsReporter.

Currently the reporter is disabled. When enabled, it will trigger a
crash to upload diagnostics when its Report method is invoked. The
upload mimics the update_engine implementation.

BUG=chromium-os:35948
TEST=unit tests, gmerged shill, ensured connectivity

Change-Id: I0353e7d694dc3bc1b5b24d27856c3b9aeda43232
Reviewed-on: https://gerrit.chromium.org/gerrit/37525
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/Makefile b/Makefile
index 6e3ee18..3d6198a 100644
--- a/Makefile
+++ b/Makefile
@@ -178,6 +178,7 @@
 	dhcp_config.o \
 	dhcp_provider.o \
 	dhcpcd_proxy.o \
+	diagnostics_reporter.o \
 	dns_client.o \
 	endpoint.o \
 	ephemeral_profile.o \
@@ -308,6 +309,7 @@
 	device_unittest.o \
 	dhcp_config_unittest.o \
 	dhcp_provider_unittest.o \
+	diagnostics_reporter_unittest.o \
 	dns_client_unittest.o \
 	error_unittest.o \
 	ethernet_service_unittest.o \
diff --git a/diagnostics_reporter.cc b/diagnostics_reporter.cc
new file mode 100644
index 0000000..2c09ad0
--- /dev/null
+++ b/diagnostics_reporter.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/diagnostics_reporter.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <base/bind.h>
+
+#include "shill/event_dispatcher.h"
+
+using base::Bind;
+
+namespace shill {
+
+namespace {
+
+base::LazyInstance<DiagnosticsReporter> g_reporter = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+DiagnosticsReporter::DiagnosticsReporter()
+    : dispatcher_(NULL),
+      weak_ptr_factory_(this) {}
+
+DiagnosticsReporter::~DiagnosticsReporter() {}
+
+// static
+DiagnosticsReporter *DiagnosticsReporter::GetInstance() {
+  return g_reporter.Pointer();
+}
+
+void DiagnosticsReporter::Init(EventDispatcher *dispatcher) {
+  dispatcher_ = dispatcher;
+}
+
+void DiagnosticsReporter::Report() {
+  // Trigger the crash from the main event loop to try to ensure minimal and
+  // consistent stack trace. TODO(petkov): Look into invoking crash_reporter
+  // directly without actually triggering a crash.
+  dispatcher_->PostTask(Bind(&DiagnosticsReporter::TriggerCrash,
+                             weak_ptr_factory_.GetWeakPtr()));
+}
+
+bool DiagnosticsReporter::IsReportingEnabled() {
+  // TODO(petkov): Implement this when there's a way to control reporting
+  // through policy. crosbug.com/35946.
+  return false;
+}
+
+void DiagnosticsReporter::TriggerCrash() {
+  if (!IsReportingEnabled()) {
+    return;
+  }
+  pid_t pid = fork();
+  if (pid < 0) {
+    PLOG(ERROR) << "fork() failed.";
+    NOTREACHED();
+    return;
+  }
+  if (pid == 0) {
+    // Crash the child.
+    abort();
+  }
+  // The parent waits for the child to terminate.
+  pid_t result = waitpid(pid, NULL, 0);
+  PLOG_IF(ERROR, result < 0) << "waitpid() failed.";
+}
+
+}  // namespace shill
diff --git a/diagnostics_reporter.h b/diagnostics_reporter.h
new file mode 100644
index 0000000..94ec9d8
--- /dev/null
+++ b/diagnostics_reporter.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_DIAGNOSTICS_REPORTER_H_
+#define SHILL_DIAGNOSTICS_REPORTER_H_
+
+#include <base/lazy_instance.h>
+#include <base/memory/weak_ptr.h>
+
+namespace shill {
+
+class EventDispatcher;
+
+class DiagnosticsReporter {
+ public:
+  virtual ~DiagnosticsReporter();
+
+  // This is a singleton -- use DiagnosticsReporter::GetInstance()->Foo()
+  static DiagnosticsReporter *GetInstance();
+
+  void Init(EventDispatcher *dispatcher);
+
+  void Report();
+
+ protected:
+  DiagnosticsReporter();
+
+  virtual bool IsReportingEnabled();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<DiagnosticsReporter>;
+  friend class DiagnosticsReporterTest;
+
+  void TriggerCrash();
+
+  EventDispatcher *dispatcher_;
+  base::WeakPtrFactory<DiagnosticsReporter> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DiagnosticsReporter);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_DIAGNOSTICS_REPORTER_H_
diff --git a/diagnostics_reporter_unittest.cc b/diagnostics_reporter_unittest.cc
new file mode 100644
index 0000000..3445571
--- /dev/null
+++ b/diagnostics_reporter_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/diagnostics_reporter.h"
+
+#include <base/message_loop.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "shill/event_dispatcher.h"
+
+using testing::Return;
+
+namespace shill {
+
+class DiagnosticsReporterTest : public testing::Test {
+ public:
+  DiagnosticsReporterTest() {}
+
+ protected:
+  bool IsReportingEnabled() {
+    return DiagnosticsReporter::GetInstance()->IsReportingEnabled();
+  }
+};
+
+namespace {
+
+class ReporterUnderTest : public DiagnosticsReporter {
+ public:
+  ReporterUnderTest() {}
+
+  MOCK_METHOD0(IsReportingEnabled, bool());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ReporterUnderTest);
+};
+
+}  // namespace
+
+TEST_F(DiagnosticsReporterTest, Report) {
+  // The test is pretty basic but covers the main flow and ensures that the main
+  // process doesn't crash.
+  ReporterUnderTest reporter;
+  EXPECT_CALL(reporter, IsReportingEnabled())
+      .WillOnce(Return(false))
+      .WillOnce(Return(true));
+  EventDispatcher dispatcher;
+  reporter.Init(&dispatcher);
+  reporter.Report();
+  reporter.Report();
+  dispatcher.PostTask(MessageLoop::QuitClosure());
+  dispatcher.DispatchForever();
+}
+
+TEST_F(DiagnosticsReporterTest, IsReportingEnabled) {
+  EXPECT_FALSE(IsReportingEnabled());
+}
+
+}  // namespace shill
diff --git a/shill_daemon.cc b/shill_daemon.cc
index be82903..b8a09ae 100644
--- a/shill_daemon.cc
+++ b/shill_daemon.cc
@@ -15,6 +15,7 @@
 #include "shill/callback80211_metrics.h"
 #include "shill/config80211.h"
 #include "shill/dhcp_provider.h"
+#include "shill/diagnostics_reporter.h"
 #include "shill/error.h"
 #include "shill/logging.h"
 #include "shill/nss.h"
@@ -99,6 +100,7 @@
   rtnl_handler_->Start(&dispatcher_, &sockets_);
   routing_table_->Start();
   dhcp_provider_->Init(control_, &dispatcher_, &glib_);
+  DiagnosticsReporter::GetInstance()->Init(&dispatcher_);
 
   if (config80211_) {
     config80211_->Init(&dispatcher_);