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