ftrace_reader: Mock code that runs atrace for testing

Change-Id: I5601e87fc03ba0b9b499d7224a6ec231230ca9e4
diff --git a/Android.bp b/Android.bp
index aa0101a..a451202 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/base/watchdog.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/event_info.cc",
     "src/ftrace_reader/event_info_constants.cc",
@@ -227,6 +228,7 @@
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/base/watchdog.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/end_to_end_integrationtest.cc",
     "src/ftrace_reader/event_info.cc",
@@ -2958,6 +2960,7 @@
     "src/base/watchdog.cc",
     "src/base/watchdog_unittest.cc",
     "src/base/weak_ptr_unittest.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/cpu_reader_unittest.cc",
     "src/ftrace_reader/event_info.cc",
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index 5fffdb5..29a24b3 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -112,6 +112,8 @@
     "../protozero",
   ]
   sources = [
+    "atrace_wrapper.cc",
+    "atrace_wrapper.h",
     "cpu_reader.cc",
     "cpu_reader.h",
     "event_info.cc",
diff --git a/src/ftrace_reader/atrace_wrapper.cc b/src/ftrace_reader/atrace_wrapper.cc
new file mode 100644
index 0000000..8ca9b7a
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "atrace_wrapper.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+
+namespace {
+
+RunAtraceFunction g_run_atrace_for_testing = nullptr;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Args should include "atrace" for argv[0].
+bool ExecvAtrace(const std::vector<std::string>& args) {
+  int status = 1;
+
+  std::vector<char*> argv;
+  // args, and then a null.
+  argv.reserve(1 + args.size());
+  for (const auto& arg : args)
+    argv.push_back(const_cast<char*>(arg.c_str()));
+  argv.push_back(nullptr);
+
+  pid_t pid = fork();
+  PERFETTO_CHECK(pid >= 0);
+  if (pid == 0) {
+    // Close stdin/out/err + any file descriptor that we might have mistakenly
+    // not marked as FD_CLOEXEC.
+    for (int i = 0; i < 128; i++)
+      close(i);
+    execv("/system/bin/atrace", &argv[0]);
+    // Reached only if execv fails.
+    _exit(1);
+  }
+  PERFETTO_EINTR(waitpid(pid, &status, 0));
+  return status == 0;
+}
+#endif
+
+}  // namespace
+
+bool RunAtrace(const std::vector<std::string>& args) {
+  if (g_run_atrace_for_testing)
+    return g_run_atrace_for_testing(args);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  return ExecvAtrace(args);
+#else
+  PERFETTO_LOG("Atrace only supported on Android.");
+  return false;
+#endif
+}
+
+void SetRunAtraceForTesting(RunAtraceFunction f) {
+  g_run_atrace_for_testing = f;
+}
+
+}  // namespace perfetto
diff --git a/src/ftrace_reader/atrace_wrapper.h b/src/ftrace_reader/atrace_wrapper.h
new file mode 100644
index 0000000..1984525
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_FTRACE_READER_ATRACE_WRAPPER_H_
+#define SRC_FTRACE_READER_ATRACE_WRAPPER_H_
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace perfetto {
+
+using RunAtraceFunction =
+    std::add_pointer<bool(const std::vector<std::string>& /*args*/)>::type;
+
+bool RunAtrace(const std::vector<std::string>& args);
+void SetRunAtraceForTesting(RunAtraceFunction);
+
+}  // namespace perfetto
+
+#endif  // SRC_FTRACE_READER_ATRACE_WRAPPER_H_
diff --git a/src/ftrace_reader/ftrace_config_muxer.cc b/src/ftrace_reader/ftrace_config_muxer.cc
index 63c1f6f..5a1ed14 100644
--- a/src/ftrace_reader/ftrace_config_muxer.cc
+++ b/src/ftrace_reader/ftrace_config_muxer.cc
@@ -16,16 +16,14 @@
 
 #include "ftrace_config_muxer.h"
 
-#include <fcntl.h>
 #include <stdint.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
 
+#include "atrace_wrapper.h"
 #include "perfetto/base/utils.h"
 #include "proto_translation_table.h"
 
@@ -47,32 +45,6 @@
   return result;
 }
 
-// Including "atrace" for argv[0].
-bool RunAtrace(const std::vector<std::string>& args) {
-  int status = 1;
-
-  std::vector<char*> argv;
-  // args, and then a null.
-  argv.reserve(1 + args.size());
-  for (const auto& arg : args)
-    argv.push_back(const_cast<char*>(arg.c_str()));
-  argv.push_back(nullptr);
-
-  pid_t pid = fork();
-  PERFETTO_CHECK(pid >= 0);
-  if (pid == 0) {
-    // Close stdin/out/err + any file descriptor that we might have mistakenly
-    // not marked as FD_CLOEXEC.
-    for (int i = 0; i < 128; i++)
-      close(i);
-    execv("/system/bin/atrace", &argv[0]);
-    // Reached only if execv fails.
-    _exit(1);
-  }
-  PERFETTO_EINTR(waitpid(pid, &status, 0));
-  return status == 0;
-}
-
 }  // namespace
 
 std::set<std::string> GetFtraceEvents(const FtraceConfig& request) {
@@ -119,7 +91,7 @@
 
     // If we're about to turn tracing on use this opportunity do some setup:
     if (RequiresAtrace(request))
-      EnableAtraceOnAndroid(request);
+      EnableAtrace(request);
     SetupClock(request);
     SetupBufferSize(request);
   } else {
@@ -188,7 +160,7 @@
     ftrace_->ClearTrace();
     current_state_.tracing_on = false;
     if (current_state_.atrace_on)
-      DisableAtraceOnAndroid();
+      DisableAtrace();
   }
 
   return true;
@@ -221,19 +193,11 @@
   current_state_.cpu_buffer_size_pages = pages;
 }
 
-void FtraceConfigMuxer::EnableAtraceOnAndroid(const FtraceConfig& request) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  EnableAtrace(request);
-#else
-  PERFETTO_LOG("Atrace only supported on Android.");
-#endif
-}
-
 void FtraceConfigMuxer::EnableAtrace(const FtraceConfig& request) {
   PERFETTO_DCHECK(!current_state_.atrace_on);
-  current_state_.atrace_on = true;
 
   PERFETTO_DLOG("Start atrace...");
+
   std::vector<std::string> args;
   args.push_back("atrace");  // argv0 for exec()
   args.push_back("--async_start");
@@ -245,26 +209,21 @@
       args.push_back(app);
   }
 
-  PERFETTO_CHECK(RunAtrace(args));
-  PERFETTO_DLOG("...done");
-}
+  if (RunAtrace(args))
+    current_state_.atrace_on = true;
 
-void FtraceConfigMuxer::DisableAtraceOnAndroid() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  DisableAtrace();
-#else
-  PERFETTO_LOG("Atrace only supported on Android.");
-#endif
+  PERFETTO_DLOG("...done");
 }
 
 void FtraceConfigMuxer::DisableAtrace() {
-  PERFETTO_DCHECK(!current_state_.atrace_on);
+  PERFETTO_DCHECK(current_state_.atrace_on);
 
   PERFETTO_DLOG("Stop atrace...");
-  PERFETTO_CHECK(RunAtrace({"atrace", "--async_stop"}));
-  PERFETTO_DLOG("...done");
 
-  current_state_.atrace_on = false;
+  if (RunAtrace({"atrace", "--async_stop"}))
+    current_state_.atrace_on = false;
+
+  PERFETTO_DLOG("...done");
 }
 
 }  // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_config_muxer.h b/src/ftrace_reader/ftrace_config_muxer.h
index 37d031b..c24609c 100644
--- a/src/ftrace_reader/ftrace_config_muxer.h
+++ b/src/ftrace_reader/ftrace_config_muxer.h
@@ -38,7 +38,8 @@
 // |RemoveConfig|.
 class FtraceConfigMuxer {
  public:
-  // The ProtoTranslationTable table should outlive this instance.
+  // The FtraceConfigMuxer and ProtoTranslationTable
+  // should outlive this instance.
   FtraceConfigMuxer(FtraceProcfs* ftrace, const ProtoTranslationTable* table);
   virtual ~FtraceConfigMuxer();
 
@@ -79,8 +80,6 @@
 
   void SetupClock(const FtraceConfig& request);
   void SetupBufferSize(const FtraceConfig& request);
-  void EnableAtraceOnAndroid(const FtraceConfig& request);
-  void DisableAtraceOnAndroid();
   void EnableAtrace(const FtraceConfig& request);
   void DisableAtrace();
 
diff --git a/src/ftrace_reader/ftrace_config_muxer_unittest.cc b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
index f7e3e74..b0ca96c 100644
--- a/src/ftrace_reader/ftrace_config_muxer_unittest.cc
+++ b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
@@ -18,14 +18,18 @@
 
 #include <memory>
 
-#include "ftrace_procfs.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+
+#include "atrace_wrapper.h"
+#include "ftrace_procfs.h"
 #include "proto_translation_table.h"
 
 using testing::_;
 using testing::AnyNumber;
 using testing::Contains;
+using testing::ElementsAreArray;
+using testing::Eq;
 using testing::IsEmpty;
 using testing::NiceMock;
 using testing::Not;
@@ -52,6 +56,20 @@
   MOCK_CONST_METHOD0(NumberOfCpus, size_t());
 };
 
+struct MockRunAtrace {
+  MockRunAtrace() {
+    static MockRunAtrace* instance;
+    instance = this;
+    SetRunAtraceForTesting([](const std::vector<std::string>& args) {
+      return instance->RunAtrace(args);
+    });
+  }
+
+  ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
+
+  MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
+};
+
 std::unique_ptr<ProtoTranslationTable> CreateFakeTable() {
   std::vector<Field> common_fields;
   std::vector<Event> events;
@@ -157,15 +175,10 @@
   ASSERT_FALSE(id);
 }
 
-// TODO(hjd): Mock atrace on Android.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_Atrace DISABLED_Atrace
-#else
-#define MAYBE_Atrace Atrace
-#endif
-TEST(FtraceConfigMuxerTest, MAYBE_Atrace) {
+TEST(FtraceConfigMuxerTest, Atrace) {
   std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
   NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
 
   FtraceConfig config = CreateFtraceConfig({"sched_switch"});
   *config.add_atrace_categories() = "sched";
@@ -174,6 +187,9 @@
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray({"atrace", "--async_start", "sched"})))
+      .WillOnce(Return(true));
 
   FtraceConfigId id = model.RequestConfig(config);
   ASSERT_TRUE(id);
@@ -183,6 +199,8 @@
   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched_switch"));
   EXPECT_THAT(actual_config->ftrace_events(), Contains("print"));
 
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_stop"})))
+      .WillOnce(Return(true));
   ASSERT_TRUE(model.RemoveConfig(id));
 }
 
diff --git a/src/ftrace_reader/ftrace_controller_unittest.cc b/src/ftrace_reader/ftrace_controller_unittest.cc
index 5eed521..964baff 100644
--- a/src/ftrace_reader/ftrace_controller_unittest.cc
+++ b/src/ftrace_reader/ftrace_controller_unittest.cc
@@ -260,8 +260,9 @@
         std::unique_ptr<MockFtraceProcfs>(new MockFtraceProcfs(cpu_count));
   }
 
-  MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
   auto model = FakeModel(ftrace_procfs.get(), table.get());
+
+  MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
   return std::unique_ptr<TestFtraceController>(new TestFtraceController(
       std::move(ftrace_procfs), std::move(table), std::move(model),
       std::move(runner), raw_procfs));