Add ftrace_controller

Change-Id: I4f1871c6e36f45d3d545f6d82dfcdf339d80429c
diff --git a/BUILD.gn b/BUILD.gn
index 614289c..ed2f9b7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -28,6 +28,7 @@
   testonly = true
   deps = [
     "//base:base_unittests",
+    "//ftrace_reader:ftrace_reader_integrationtests",
     "//ftrace_reader:ftrace_reader_unittests",
     "//protozero:protozero_unittests",
     "//tools/ftrace_proto_gen:ftrace_proto_gen_unittests",
diff --git a/ftrace_reader/BUILD.gn b/ftrace_reader/BUILD.gn
index 96b614d..3dee6f0 100644
--- a/ftrace_reader/BUILD.gn
+++ b/ftrace_reader/BUILD.gn
@@ -29,6 +29,21 @@
   ]
 }
 
+# These tests require access to a real ftrace implementation and must
+# run with sudo.
+executable("ftrace_reader_integrationtests") {
+  testonly = true
+  deps += [
+    ":ftrace_reader",
+    "//buildtools:gmock",
+    "//buildtools:gtest",
+    "//buildtools:gtest_main",
+  ]
+  sources = [
+    "src/ftrace_controller_integrationtest.cc",
+  ]
+}
+
 executable("ftrace_reader_demo") {
   sources = [
     "main.cc",
@@ -38,11 +53,11 @@
 
 source_set("ftrace_reader") {
   all_dependent_configs = [ ":ftrace_reader_config" ]
-  deps += [
-    "//protozero",
-  ]
+  deps += [ "//protozero" ]
   sources = [
+    "include/ftrace_reader/ftrace_controller.h",
     "include/ftrace_reader/ftrace_cpu_reader.h",
+    "src/ftrace_controller.cc",
     "src/ftrace_cpu_reader.cc",
   ]
 }
diff --git a/ftrace_reader/include/ftrace_reader/ftrace_controller.h b/ftrace_reader/include/ftrace_reader/ftrace_controller.h
new file mode 100644
index 0000000..264a2c2
--- /dev/null
+++ b/ftrace_reader/include/ftrace_reader/ftrace_controller.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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 FTRACE_READER_FTRACE_CONTROLLER_H_
+#define FTRACE_READER_FTRACE_CONTROLLER_H_
+
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace perfetto {
+
+// Utility class for controling ftrace.
+class FtraceController {
+ public:
+  FtraceController();
+
+  // Clears the trace buffers for all CPUs. Blocks until this is done.
+  void ClearTrace();
+
+  // Writes the string |str| as an event into the trace buffer.
+  bool WriteTraceMarker(const std::string& str);
+
+  // Enable tracing.
+  bool EnableTracing();
+
+  // Disables tracing, does not clear the buffer.
+  bool DisableTracing();
+
+  // Returns true iff tracing is enabled.
+  // Necessarily racy: another program could enable/disable tracing at any
+  // point.
+  bool IsTracingEnabled();
+
+  // Enable the event |name|.
+  bool EnableEvent(const std::string& name);
+
+  // Disable the event |name|.
+  bool DisableEvent(const std::string& name);
+
+ private:
+  FtraceController(const FtraceController&) = delete;
+  FtraceController& operator=(const FtraceController&) = delete;
+};
+
+}  // namespace perfetto
+
+#endif  // FTRACE_READER_FTRACE_CONTROLLER_H_
diff --git a/ftrace_reader/main.cc b/ftrace_reader/main.cc
index cc396f8..26268a6 100644
--- a/ftrace_reader/main.cc
+++ b/ftrace_reader/main.cc
@@ -15,11 +15,29 @@
  */
 
 #include <stdio.h>
+#include <unistd.h>
 
+#include "ftrace_reader/ftrace_controller.h"
 #include "ftrace_reader/ftrace_cpu_reader.h"
 
 int main(int argc, const char** argv) {
-  if (argc > 1)
-    printf("Usage: %s\n", argv[0]);
+  perfetto::FtraceController ftrace_controller;
+
+  ftrace_controller.ClearTrace();
+  ftrace_controller.WriteTraceMarker("Hello, world!");
+
+  for (int i = 1; i < argc; i++) {
+    printf("Enabling: %s\n", argv[i]);
+    ftrace_controller.EnableEvent(argv[i]);
+  }
+
+  // Sleep for one second so we get some events
+  sleep(1);
+
+  for (int i = 1; i < argc; i++) {
+    printf("Disable: %s\n", argv[i]);
+    ftrace_controller.DisableEvent(argv[i]);
+  }
+
   return perfetto::SomePublicApi();
 }
diff --git a/ftrace_reader/src/ftrace_controller.cc b/ftrace_reader/src/ftrace_controller.cc
new file mode 100644
index 0000000..b0d3a19
--- /dev/null
+++ b/ftrace_reader/src/ftrace_controller.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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 "ftrace_reader/ftrace_controller.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/scoped_file.h"
+#include "base/utils.h"
+
+namespace perfetto {
+
+namespace {
+
+// TODO(b/68242551): Do not hardcode these paths.
+// This directory contains the 'format' and 'enable' files for each event.
+// These are nested like so: group_name/event_name/{format, enable}
+const char kTraceEventPath[] = "/sys/kernel/debug/tracing/events/";
+
+// Reading this file produces human readable trace output.
+// Writing to this file clears all trace buffers for all CPUS.
+const char kTracePath[] = "/sys/kernel/debug/tracing/trace";
+
+// Writing to this file injects an event into the trace buffer.
+const char kTraceMarkerPath[] = "/sys/kernel/debug/tracing/trace_marker";
+
+// Reading this file returns 1/0 if tracing is enabled/disabled.
+// Writing 1/0 to this file enables/disables tracing.
+// Disabling tracing with this file prevents further writes but
+// does not clear the buffer.
+const char kTracingOnPath[] = "/sys/kernel/debug/tracing/tracing_on";
+
+bool WriteToFile(const std::string& path, const std::string& str) {
+  base::ScopedFile fd(open(path.c_str(), O_WRONLY));
+  if (!fd)
+    return false;
+  ssize_t written = PERFETTO_EINTR(write(fd.get(), str.c_str(), str.length()));
+  ssize_t length = static_cast<ssize_t>(str.length());
+  // This should either fail or write fully.
+  PERFETTO_DCHECK(written == length || written == -1);
+  return written == length;
+}
+
+char ReadOneCharFromFile(const std::string& path) {
+  base::ScopedFile fd(open(path.c_str(), O_RDONLY));
+  if (!fd)
+    return '\0';
+  char result = '\0';
+  ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
+  PERFETTO_DCHECK(bytes == 1 || bytes == -1);
+  return result;
+}
+
+}  // namespace
+
+FtraceController::FtraceController() {}
+
+void FtraceController::ClearTrace() {
+  base::ScopedFile fd(open(kTracePath, O_WRONLY | O_TRUNC));
+  PERFETTO_CHECK(fd);  // Could not clear.
+}
+
+bool FtraceController::WriteTraceMarker(const std::string& str) {
+  return WriteToFile(kTraceMarkerPath, str);
+}
+
+bool FtraceController::EnableTracing() {
+  return WriteToFile(kTracingOnPath, "1");
+}
+
+bool FtraceController::DisableTracing() {
+  return WriteToFile(kTracingOnPath, "0");
+}
+
+bool FtraceController::IsTracingEnabled() {
+  return ReadOneCharFromFile(kTracingOnPath) == '1';
+}
+
+bool FtraceController::EnableEvent(const std::string& name) {
+  std::string path = std::string(kTraceEventPath) + name + "/enable";
+  return WriteToFile(path, "1");
+}
+
+bool FtraceController::DisableEvent(const std::string& name) {
+  std::string path = std::string(kTraceEventPath) + name + "/enable";
+  return WriteToFile(path, "0");
+}
+
+}  // namespace perfetto
diff --git a/ftrace_reader/src/ftrace_controller_integrationtest.cc b/ftrace_reader/src/ftrace_controller_integrationtest.cc
new file mode 100644
index 0000000..817b72a
--- /dev/null
+++ b/ftrace_reader/src/ftrace_controller_integrationtest.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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 <fstream>
+#include <sstream>
+
+#include "ftrace_reader/ftrace_controller.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using testing::HasSubstr;
+using testing::Not;
+
+namespace perfetto {
+namespace {
+
+const char kTracePath[] = "/sys/kernel/debug/tracing/trace";
+
+std::string GetTraceOutput() {
+  std::ifstream fin(kTracePath, std::ios::in);
+  if (!fin) {
+    ADD_FAILURE() << "Could not read trace output";
+    return "";
+  }
+  std::ostringstream stream;
+  stream << fin.rdbuf();
+  fin.close();
+  return stream.str();
+}
+
+TEST(FtraceController, ClearTrace) {
+  FtraceController ftrace_controller;
+  ftrace_controller.WriteTraceMarker("Hello, World!");
+  ftrace_controller.ClearTrace();
+  EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("Hello, World!")));
+}
+
+TEST(FtraceController, TraceMarker) {
+  FtraceController ftrace_controller;
+  ftrace_controller.WriteTraceMarker("Hello, World!");
+  EXPECT_THAT(GetTraceOutput(), HasSubstr("Hello, World!"));
+}
+
+TEST(FtraceController, EnableDisableEvent) {
+  FtraceController ftrace_controller;
+  ftrace_controller.EnableEvent("sched/sched_switch");
+  sleep(1);
+  EXPECT_THAT(GetTraceOutput(), HasSubstr("sched_switch"));
+
+  ftrace_controller.DisableEvent("sched/sched_switch");
+  ftrace_controller.ClearTrace();
+  sleep(1);
+  EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("sched_switch")));
+}
+
+TEST(FtraceController, EnableDisableTracing) {
+  FtraceController ftrace_controller;
+  ftrace_controller.ClearTrace();
+  EXPECT_TRUE(ftrace_controller.IsTracingEnabled());
+  ftrace_controller.WriteTraceMarker("Before");
+  ftrace_controller.DisableTracing();
+  EXPECT_FALSE(ftrace_controller.IsTracingEnabled());
+  ftrace_controller.WriteTraceMarker("During");
+  ftrace_controller.EnableTracing();
+  EXPECT_TRUE(ftrace_controller.IsTracingEnabled());
+  ftrace_controller.WriteTraceMarker("After");
+  EXPECT_THAT(GetTraceOutput(), HasSubstr("Before"));
+  EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("During")));
+  EXPECT_THAT(GetTraceOutput(), HasSubstr("After"));
+}
+
+}  // namespace
+}  // namespace perfetto