Add metatracing to trace perfetto itself.

Bug: 78765090
Change-Id: Ic1514d4040a78175cc9fd91526f26fc2d6937d5c
diff --git a/Android.bp b/Android.bp
index 59bc206..501bc8e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,6 +31,7 @@
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
@@ -144,6 +145,7 @@
     ":perfetto_src_perfetto_cmd_protos_gen",
     "src/base/android_task_runner.cc",
     "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
@@ -271,6 +273,7 @@
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/android_task_runner.cc",
     "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
@@ -3454,6 +3457,7 @@
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
@@ -3626,6 +3630,7 @@
     ":perfetto_src_protozero_testing_messages_zero_gen",
     "src/base/android_task_runner.cc",
     "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
     "src/base/page_allocator_unittest.cc",
     "src/base/scoped_file_unittest.cc",
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index 985824c..41f075d 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -17,6 +17,7 @@
     "build_config.h",
     "file_utils.h",
     "logging.h",
+    "metatrace.h",
     "page_allocator.h",
     "scoped_file.h",
     "small_set.h",
diff --git a/include/perfetto/base/metatrace.h b/include/perfetto/base/metatrace.h
new file mode 100644
index 0000000..4bd38a1
--- /dev/null
+++ b/include/perfetto/base/metatrace.h
@@ -0,0 +1,77 @@
+/*
+ * 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 INCLUDE_PERFETTO_BASE_METATRACE_H_
+#define INCLUDE_PERFETTO_BASE_METATRACE_H_
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/utils.h"
+
+namespace perfetto {
+namespace base {
+
+template <typename T>
+std::string FormatJSON(T value) {
+  return std::to_string(value);
+}
+template <>
+std::string FormatJSON<std::string>(std::string value);
+template <>
+std::string FormatJSON<const char*>(const char* value);
+
+int MaybeOpenTraceFile();
+
+class MetaTrace {
+ public:
+  template <typename... Ts>
+  MetaTrace(Ts... args) {
+    AddElements(args...);
+    WriteEvent("B");
+  }
+
+  template <typename T, typename... Ts>
+  void AddElements(const char* name, T arg, Ts... args) {
+    trace_.emplace_back(FormatJSON(name), FormatJSON(std::move(arg)));
+    AddElements(args...);
+  }
+
+  template <typename T>
+  void AddElements(const char* name, T arg) {
+    trace_.emplace_back(FormatJSON(name), FormatJSON(std::move(arg)));
+  }
+
+  ~MetaTrace() { WriteEvent("E"); }
+
+ private:
+  void WriteEvent(std::string type);
+
+  std::vector<std::pair<std::string, std::string>> trace_;
+};
+
+#if PERFETTO_DCHECK_IS_ON() && !PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
+#define PERFETTO_METATRACE(...) ::perfetto::base::MetaTrace(__VA_ARGS__)
+#else
+#define PERFETTO_METATRACE(...) ::perfetto::base::ignore_result(__VA_ARGS__)
+#endif
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_BASE_METATRACE_H_
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 1d05b75..e62605a 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -24,12 +24,14 @@
   ]
   sources = [
     "file_utils.cc",
+    "metatrace.cc",
     "string_splitter.cc",
     "string_utils.cc",
     "thread_checker.cc",
     "time.cc",
     "virtual_destructors.cc",
   ]
+
   # TODO(brucedawson): Enable these for Windows when possible.
   if (!is_win) {
     sources += [
@@ -106,6 +108,7 @@
     "../../gn:default_deps",
     "../../gn:gtest_deps",
   ]
+
   # TODO(brucedawson): Enable these for Windows when possible.
   if (!is_win) {
     deps += [ ":test_support" ]
@@ -120,6 +123,7 @@
     "time_unittest.cc",
     "weak_ptr_unittest.cc",
   ]
+
   # TODO(brucedawson): Enable these for Windows when possible.
   if (!is_win) {
     sources += [
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
new file mode 100644
index 0000000..ab86dd1
--- /dev/null
+++ b/src/base/metatrace.cc
@@ -0,0 +1,65 @@
+/*
+ * 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 "perfetto/base/metatrace.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "perfetto/base/time.h"
+
+namespace perfetto {
+namespace base {
+
+int MaybeOpenTraceFile() {
+  static const char* tracing_path = getenv("PERFETTO_METATRACE_FILE");
+  if (tracing_path == nullptr)
+    return -1;
+  static int fd = open(tracing_path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
+  return fd;
+}
+
+template <>
+std::string FormatJSON<std::string>(std::string value) {
+  return "\"" + value + "\"";
+}
+
+template <>
+std::string FormatJSON<const char*>(const char* value) {
+  return std::string("\"") + value + "\"";
+}
+
+void MetaTrace::WriteEvent(std::string type) {
+  int fd = MaybeOpenTraceFile();
+  if (fd == -1)
+    return;
+
+  std::string data = "{";
+  data.reserve(128);
+  for (size_t i = 0; i < trace_.size(); ++i) {
+    const std::pair<std::string, std::string>& p = trace_[i];
+    data += p.first;
+    data += ": ";
+    data += p.second;
+    data += ", ";
+  }
+  data += "\"ts\": " + std::to_string(GetWallTimeNs().count() / 1000.) +
+          ", \"cat\": \"PERF\", \"ph\": \"" + type + "\"},\n";
+  ignore_result(write(fd, data.c_str(), data.size()));
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/ftrace_reader/cpu_reader.cc b/src/ftrace_reader/cpu_reader.cc
index 3014bc4..0e718ab 100644
--- a/src/ftrace_reader/cpu_reader.cc
+++ b/src/ftrace_reader/cpu_reader.cc
@@ -26,6 +26,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/base/metatrace.h"
 #include "perfetto/base/utils.h"
 #include "src/ftrace_reader/proto_translation_table.h"
 
@@ -209,8 +210,12 @@
     // First do a blocking splice which sleeps until there is at least one
     // page of data available and enough space to write it into the staging
     // pipe.
-    ssize_t splice_res = splice(trace_fd, nullptr, staging_write_fd, nullptr,
-                                base::kPageSize, SPLICE_F_MOVE);
+    ssize_t splice_res;
+    {
+      PERFETTO_METATRACE("name", "splice_blocking", "pid", cpu);
+      splice_res = splice(trace_fd, nullptr, staging_write_fd, nullptr,
+                          base::kPageSize, SPLICE_F_MOVE);
+    }
     if (splice_res < 0) {
       // The kernel ftrace code has its own splice() implementation that can
       // occasionally fail with transient errors not reported in man 2 splice.
@@ -228,17 +233,22 @@
     // pages from the trace pipe into the staging pipe as long as there is
     // data in the former and space in the latter.
     while (true) {
-      splice_res = splice(trace_fd, nullptr, staging_write_fd, nullptr,
-                          base::kPageSize, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+      {
+        PERFETTO_METATRACE("name", "splice_nonblocking", "pid", cpu);
+        splice_res = splice(trace_fd, nullptr, staging_write_fd, nullptr,
+                            base::kPageSize, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+      }
       if (splice_res < 0) {
         if (errno != EAGAIN && errno != ENOMEM && errno != EBUSY)
           PERFETTO_PLOG("splice");
         break;
       }
     }
-
-    // This callback will block until we are allowed to read more data.
-    on_data_available();
+    {
+      PERFETTO_METATRACE("name", "splice_waitcallback", "pid", cpu);
+      // This callback will block until we are allowed to read more data.
+      on_data_available();
+    }
   }
 #else
   base::ignore_result(cpu);
diff --git a/tools/tmux b/tools/tmux
index 9a14356..c1e3366 100755
--- a/tools/tmux
+++ b/tools/tmux
@@ -108,6 +108,8 @@
   push $OUT/libtraced_shared.so
 fi
 
+PREFIX="$PREFIX PERFETTO_METATRACE_FILE=$DIR/mtrace"
+
 CONFIG_DEVICE_PATH=$CONFIG
 if [[ "$CONFIG" != ":test" ]]; then
   CONFIG_DEVICE_PATH=$DIR/$CONFIG.protobuf
@@ -176,6 +178,11 @@
 
 TRACE=$HOME/Downloads/trace
 pull trace /tmp/trace.protobuf
+pull mtrace /tmp/mtrace.json
+# Add [ to beginning of file and replace trailing , with ] to turn into valid
+# JSON array.
+sed -i '$ s/.$/]/' /tmp/mtrace.json
+sed -i '1s/^/[/' /tmp/mtrace.json
 echo -e "\n\x1b[32mPulling trace into $TRACE.pbtext\x1b[0m"
 $OUT/trace_to_text text < /tmp/trace.protobuf > $TRACE.pbtext
 echo -e "\n\x1b[32mPulling trace into $TRACE.json\x1b[0m"