Add tool to analyze rate of events.

Bug: 78765090
Change-Id: I7c7845c2b3cab41e4c925938cd07d77c04ac66f5
diff --git a/Android.bp b/Android.bp
index 0579556..59bc206 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,7 @@
     "src/base/string_utils.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
+    "src/base/time.cc",
     "src/base/unix_task_runner.cc",
     "src/base/virtual_destructors.cc",
     "src/base/watchdog_posix.cc",
@@ -148,6 +149,7 @@
     "src/base/string_utils.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
+    "src/base/time.cc",
     "src/base/unix_task_runner.cc",
     "src/base/virtual_destructors.cc",
     "src/base/watchdog_posix.cc",
@@ -276,6 +278,7 @@
     "src/base/test/test_task_runner.cc",
     "src/base/test/vm_test_utils.cc",
     "src/base/thread_checker.cc",
+    "src/base/time.cc",
     "src/base/unix_task_runner.cc",
     "src/base/virtual_destructors.cc",
     "src/base/watchdog_posix.cc",
@@ -3456,6 +3459,7 @@
     "src/base/string_utils.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
+    "src/base/time.cc",
     "src/base/unix_task_runner.cc",
     "src/base/virtual_destructors.cc",
     "src/base/watchdog_posix.cc",
@@ -3636,6 +3640,7 @@
     "src/base/test/vm_test_utils.cc",
     "src/base/thread_checker.cc",
     "src/base/thread_checker_unittest.cc",
+    "src/base/time.cc",
     "src/base/time_unittest.cc",
     "src/base/unix_task_runner.cc",
     "src/base/utils_unittest.cc",
diff --git a/tools/BUILD.gn b/tools/BUILD.gn
index d5b6a98..1397d7e 100644
--- a/tools/BUILD.gn
+++ b/tools/BUILD.gn
@@ -31,3 +31,14 @@
     "../buildtools:protoc($host_toolchain)",
   ]
 }
+
+executable("pipestats") {
+  testonly = true
+  sources = [
+    "pipestats.cc",
+  ]
+  deps = [
+    "../gn:default_deps",
+    "../src/base",
+  ]
+}
diff --git a/tools/pipestats.cc b/tools/pipestats.cc
new file mode 100644
index 0000000..d82ecbb
--- /dev/null
+++ b/tools/pipestats.cc
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+// Print timestamps when pages were read to stdout.
+// This data is useful to compute rate of events from the kernel.
+
+#include <thread>
+
+#include <fcntl.h>  // splice
+#include <inttypes.h>
+#include <stdint.h>
+#include <unistd.h>  // pipe
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/time.h"
+#include "perfetto/base/utils.h"
+
+namespace perfetto {
+namespace {
+
+void SetBlocking(int fd, bool is_blocking) {
+  int flags = fcntl(fd, F_GETFL, 0);
+  flags = (is_blocking) ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+  PERFETTO_CHECK(fcntl(fd, F_SETFL, flags) == 0);
+}
+
+__attribute__((__noreturn__)) void ReadLoop(int fd) {
+  char buf[4096];
+  while (true) {
+    base::ignore_result(read(fd, &buf, sizeof(buf)));
+  }
+}
+
+int PipestatsMain(int argc, char** argv) {
+  PERFETTO_CHECK(argc == 2);
+  base::ScopedFile trace_fd(open(argv[1], O_RDONLY));
+  PERFETTO_CHECK(trace_fd);
+  std::thread reader(ReadLoop, trace_fd.get());
+
+  int pipe_fds[2];
+  PERFETTO_CHECK(pipe(&pipe_fds[0]) == 0);
+  base::ScopedFile staging_read_fd(pipe_fds[0]);
+  base::ScopedFile staging_write_fd(pipe_fds[1]);
+
+  // Make reads from the raw pipe blocking so that splice() can sleep.
+  SetBlocking(*trace_fd, true);
+
+  // Reads from the staging pipe are always non-blocking.
+  SetBlocking(*staging_read_fd, false);
+
+  // Note: O_NONBLOCK seems to be ignored by splice() on the target pipe. The
+  // blocking vs non-blocking behavior is controlled solely by the
+  // SPLICE_F_NONBLOCK flag passed to splice().
+  SetBlocking(*staging_write_fd, false);
+
+  while (true) {
+    ssize_t splice_res = splice(*trace_fd, nullptr, *staging_write_fd, nullptr,
+                                base::kPageSize, SPLICE_F_MOVE);
+    if (splice_res > 0) {
+      auto cur = base::GetWallTimeNs();
+      printf("%" PRId64 "\n", int64_t(cur.count()));
+    }
+  }
+}
+
+}  // namespace
+}  // namespace perfetto
+
+int main(int argc, char** argv) {
+  return perfetto::PipestatsMain(argc, argv);
+}