Merge "Revert "Rollback DAC_READ_SEARCH capability""
diff --git a/.travis.yml b/.travis.yml
index 3e89e9e..69f9cdb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -140,7 +140,7 @@
       for TEST_TARGET in $TEST_TARGETS; do
         "out/dist/$TEST_TARGET"
       done
-      bash -c "out/dist/perfetto_benchmarks --benchmark_filter=\"(\$(out/dist/perfetto_benchmarks  --benchmark_list_tests | sed \"/BM_EndToEnd\/.....*\//d\" | xargs | tr \" \" \"|\"))\""
+      BENCHMARK_FUNCTIONAL_TEST_ONLY=true out/dist/perfetto_benchmarks
       if [[ "$CFG" == *-libfuzzer ]]; then
         # Run a single iteration each to make sure they are not crashing.
         out/dist/end_to_end_shared_memory_fuzzer -runs=1
@@ -152,7 +152,7 @@
       for TEST_TARGET in $TEST_TARGETS; do
         tools/run_android_test out/dist "$TEST_TARGET"
       done
-      tools/run_android_test out/dist "perfetto_benchmarks" "--benchmark_filter=\"(\$(perfetto_benchmarks  --benchmark_list_tests | sed \"/BM_EndToEnd\/.....*\//d\" | xargs | tr \" \" \"|\"))\""
+      tools/run_android_test --env BENCHMARK_FUNCTIONAL_TEST_ONLY=true out/dist "perfetto_benchmarks"
     fi
 
 after_script:
diff --git a/Android.bp b/Android.bp
index de7759d..44ab92c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,7 +61,9 @@
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
     "src/protozero/scattered_stream_writer.cc",
+    "src/traced/probes/filesystem/file_scanner.cc",
     "src/traced/probes/filesystem/fs_mount.cc",
     "src/traced/probes/filesystem/inode_file_data_source.cc",
     "src/traced/probes/filesystem/lru_inode_cache.cc",
@@ -77,7 +79,10 @@
     "src/tracing/core/data_source_descriptor.cc",
     "src/tracing/core/ftrace_config.cc",
     "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
     "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/process_stats_config.cc",
     "src/tracing/core/service_impl.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
@@ -162,6 +167,7 @@
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
     "src/protozero/scattered_stream_writer.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
@@ -169,7 +175,10 @@
     "src/tracing/core/data_source_descriptor.cc",
     "src/tracing/core/ftrace_config.cc",
     "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
     "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/process_stats_config.cc",
     "src/tracing/core/service_impl.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
@@ -284,7 +293,6 @@
     "src/ftrace_reader/proto_translation_table.cc",
     "src/ftrace_reader/test/cpu_reader_support.cc",
     "src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc",
-    "src/ftrace_reader/test/scattered_stream_null_delegate.cc",
     "src/ipc/buffered_frame_deserializer.cc",
     "src/ipc/client_impl.cc",
     "src/ipc/deferred.cc",
@@ -296,7 +304,9 @@
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
     "src/protozero/scattered_stream_writer.cc",
+    "src/traced/probes/filesystem/file_scanner.cc",
     "src/traced/probes/filesystem/fs_mount.cc",
     "src/traced/probes/filesystem/inode_file_data_source.cc",
     "src/traced/probes/filesystem/lru_inode_cache.cc",
@@ -310,7 +320,10 @@
     "src/tracing/core/data_source_descriptor.cc",
     "src/tracing/core/ftrace_config.cc",
     "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
     "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/process_stats_config.cc",
     "src/tracing/core/service_impl.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
@@ -321,9 +334,9 @@
     "src/tracing/core/trace_packet.cc",
     "src/tracing/core/trace_writer_impl.cc",
     "test/end_to_end_integrationtest.cc",
-    "test/fake_consumer.cc",
     "test/fake_producer.cc",
     "test/task_runner_thread.cc",
+    "test/test_helper.cc",
   ],
   shared_libs: [
     "libandroid",
@@ -408,6 +421,8 @@
     "protos/perfetto/config/data_source_config.proto",
     "protos/perfetto/config/data_source_descriptor.proto",
     "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/inode_file/inode_file_config.proto",
+    "protos/perfetto/config/process_stats/process_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -420,6 +435,8 @@
     "external/perfetto/protos/perfetto/config/data_source_config.pb.cc",
     "external/perfetto/protos/perfetto/config/data_source_descriptor.pb.cc",
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.cc",
+    "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.cc",
+    "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.cc",
     "external/perfetto/protos/perfetto/config/test_config.pb.cc",
     "external/perfetto/protos/perfetto/config/trace_config.pb.cc",
   ],
@@ -433,6 +450,8 @@
     "protos/perfetto/config/data_source_config.proto",
     "protos/perfetto/config/data_source_descriptor.proto",
     "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/inode_file/inode_file_config.proto",
+    "protos/perfetto/config/process_stats/process_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -445,6 +464,8 @@
     "external/perfetto/protos/perfetto/config/data_source_config.pb.h",
     "external/perfetto/protos/perfetto/config/data_source_descriptor.pb.h",
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.h",
+    "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.h",
+    "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.h",
     "external/perfetto/protos/perfetto/config/test_config.pb.h",
     "external/perfetto/protos/perfetto/config/trace_config.pb.h",
   ],
@@ -461,6 +482,8 @@
     "protos/perfetto/config/data_source_config.proto",
     "protos/perfetto/config/data_source_descriptor.proto",
     "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/inode_file/inode_file_config.proto",
+    "protos/perfetto/config/process_stats/process_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -474,6 +497,8 @@
     "external/perfetto/protos/perfetto/config/data_source_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/data_source_descriptor.pbzero.cc",
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/test_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/trace_config.pbzero.cc",
   ],
@@ -487,6 +512,8 @@
     "protos/perfetto/config/data_source_config.proto",
     "protos/perfetto/config/data_source_descriptor.proto",
     "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/inode_file/inode_file_config.proto",
+    "protos/perfetto/config/process_stats/process_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -500,6 +527,8 @@
     "external/perfetto/protos/perfetto/config/data_source_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/data_source_descriptor.pbzero.h",
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/test_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/trace_config.pbzero.h",
   ],
@@ -3119,6 +3148,7 @@
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
     "src/protozero/scattered_stream_writer.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
@@ -3126,7 +3156,10 @@
     "src/tracing/core/data_source_descriptor.cc",
     "src/tracing/core/ftrace_config.cc",
     "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
     "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/process_stats_config.cc",
     "src/tracing/core/service_impl.cc",
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
@@ -3305,7 +3338,6 @@
     "src/ftrace_reader/proto_translation_table_unittest.cc",
     "src/ftrace_reader/test/cpu_reader_support.cc",
     "src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc",
-    "src/ftrace_reader/test/scattered_stream_null_delegate.cc",
     "src/ipc/buffered_frame_deserializer.cc",
     "src/ipc/buffered_frame_deserializer_unittest.cc",
     "src/ipc/client_impl.cc",
@@ -3329,10 +3361,12 @@
     "src/protozero/message_unittest.cc",
     "src/protozero/proto_utils.cc",
     "src/protozero/proto_utils_unittest.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
     "src/protozero/scattered_stream_writer.cc",
     "src/protozero/scattered_stream_writer_unittest.cc",
     "src/protozero/test/fake_scattered_buffer.cc",
     "src/protozero/test/protozero_conformance_unittest.cc",
+    "src/traced/probes/filesystem/file_scanner.cc",
     "src/traced/probes/filesystem/fs_mount.cc",
     "src/traced/probes/filesystem/fs_mount_unittest.cc",
     "src/traced/probes/filesystem/inode_file_data_source.cc",
@@ -3352,9 +3386,13 @@
     "src/tracing/core/ftrace_config.cc",
     "src/tracing/core/id_allocator.cc",
     "src/tracing/core/id_allocator_unittest.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
+    "src/tracing/core/null_trace_writer_unittest.cc",
     "src/tracing/core/packet_stream_validator.cc",
     "src/tracing/core/packet_stream_validator_unittest.cc",
     "src/tracing/core/patch_list_unittest.cc",
+    "src/tracing/core/process_stats_config.cc",
     "src/tracing/core/service_impl.cc",
     "src/tracing/core/service_impl_unittest.cc",
     "src/tracing/core/shared_memory_abi.cc",
@@ -3429,6 +3467,42 @@
   },
 }
 
+// GN target: //:trace_to_text
+cc_binary_host {
+  name: "trace_to_text",
+  srcs: [
+    ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_trace_chrome_lite_gen",
+    ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
+    ":perfetto_protos_perfetto_trace_ftrace_lite_gen",
+    ":perfetto_protos_perfetto_trace_lite_gen",
+    ":perfetto_protos_perfetto_trace_ps_lite_gen",
+    "tools/trace_to_text/ftrace_event_formatter.cc",
+    "tools/trace_to_text/ftrace_inode_handler.cc",
+    "tools/trace_to_text/main.cc",
+  ],
+  shared_libs: [
+    "liblog",
+    "libprotobuf-cpp-full",
+    "libprotobuf-cpp-lite",
+  ],
+  generated_headers: [
+    "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+  cflags: [
+    "-DGOOGLE_PROTOBUF_NO_RTTI",
+    "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+  ],
+}
+
 // GN target: //:traced
 cc_binary {
   name: "traced",
@@ -3475,10 +3549,10 @@
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/proto_translation_table.cc",
     "src/traced/probes/probes_producer.cc",
-    "test/fake_producer.cc",
-    "test/fake_consumer.cc",
-    "test/task_runner_thread.cc",
     "test/end_to_end_integrationtest.cc",
+    "test/fake_producer.cc",
+    "test/task_runner_thread.cc",
+    "test/test_helper.cc",
   ],
   export_include_dirs: [
     ".",
diff --git a/Android.bp.extras b/Android.bp.extras
index a3f25d5..42c389c 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -11,10 +11,10 @@
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/proto_translation_table.cc",
     "src/traced/probes/probes_producer.cc",
-    "test/fake_producer.cc",
-    "test/fake_consumer.cc",
-    "test/task_runner_thread.cc",
     "test/end_to_end_integrationtest.cc",
+    "test/fake_producer.cc",
+    "test/task_runner_thread.cc",
+    "test/test_helper.cc",
   ],
   export_include_dirs: [
     ".",
diff --git a/BUILD.gn b/BUILD.gn
index ce1eba4..1207331 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -26,20 +26,19 @@
   testonly = true  # allow to build also test targets
   deps = [
     ":perfetto_unittests",
-    "src/ipc/protoc_plugin:ipc_plugin($host_toolchain)",
     "src/protozero/protoc_plugin($host_toolchain)",
-    "tools/proto_to_cpp",
   ]
-  if (is_linux || is_android) {
-    deps += [ "tools/ftrace_proto_gen:ftrace_proto_gen" ]
-  }
   if (!build_with_chromium) {
     deps += [
       "protos/perfetto/config:merged_config",  # For syntax-checking the proto.
+      "src/ipc/protoc_plugin:ipc_plugin($host_toolchain)",
       "test/configs",
       "tools:protoc_helper",
-      "tools/trace_to_text",
+      "tools/proto_to_cpp",
     ]
+    if (!build_with_android) {
+      deps += [ "tools/trace_to_text" ]
+    }
     if (is_linux || is_android) {
       deps += [
         ":perfetto",
@@ -47,6 +46,7 @@
         ":perfetto_integrationtests",
         ":traced",
         ":traced_probes",
+        "tools/ftrace_proto_gen:ftrace_proto_gen",
         "tools/skippy",
       ]
     }
@@ -59,21 +59,24 @@
     "gn:default_deps",
     "gn:gtest_main",
     "src/base:unittests",
-    "src/ipc:unittests",
-    "src/perfetto_cmd:unittests",
     "src/protozero:unittests",
-    "src/traced/probes/filesystem:unittests",
     "src/tracing:unittests",
   ]
-  if (is_linux || is_android) {
-    deps += [
-      "src/ftrace_reader:unittests",
-      "src/traced/probes:unittests",
-      "tools/ftrace_proto_gen:unittests",
-    ]
-  }
   if (!build_with_chromium) {
-    deps += [ "tools/sanitizers_unittests" ]
+    if (is_linux || is_android) {
+      deps += [
+        "src/ftrace_reader:unittests",
+        "src/traced/probes:unittests",
+        "tools/ftrace_proto_gen:unittests",
+      ]
+    }
+
+    deps += [
+      "src/ipc:unittests",
+      "src/perfetto_cmd:unittests",
+      "src/traced/probes/filesystem:unittests",
+      "tools/sanitizers_unittests",
+    ]
   }
 }
 
@@ -164,6 +167,16 @@
     }
   }
 
+  if (build_with_android) {
+    executable("trace_to_text") {
+      testonly = true
+      deps = [
+        "gn:default_deps",
+        "tools/trace_to_text:lib",
+      ]
+    }
+  }
+
   # This target exports perfetto trace protos in the Android build system,
   # allowing both host and device targets to implement custom parsers based on
   # our protos.
diff --git a/docs/running_perfetto.md b/docs/running_perfetto.md
index ba59432..0763505 100644
--- a/docs/running_perfetto.md
+++ b/docs/running_perfetto.md
@@ -62,7 +62,7 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.ftrace"
+    name: "linux.ftrace"
     target_buffer: 0
     ftrace_config {
       buffer_size_kb: 40 # Kernel ftrace buffer size.
@@ -74,7 +74,7 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.process_stats"
+    name: "linux.process_stats"
     target_buffer: 0
   }
 }
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index d0b8a20..ce1088c 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -74,6 +74,7 @@
     cflags += [
       "-fcolor-diagnostics",
       "-Wno-unknown-warning-option",
+      "-fdiagnostics-show-template-tree",
     ]
   } else {
     cflags += [ "-Wno-unknown-warning" ]
diff --git a/include/perfetto/base/build_config.h b/include/perfetto/base/build_config.h
index b04321d..de4a436 100644
--- a/include/perfetto/base/build_config.h
+++ b/include/perfetto/base/build_config.h
@@ -24,7 +24,7 @@
 #define PERFETTO_BUILDFLAG(flag) \
   (PERFETTO_BUILDFLAG_CAT(PERFETTO_BUILDFLAG_DEFINE_, flag)())
 
-#if defined(ANDROID)
+#if defined(__ANDROID__)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 1
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MACOSX() 0
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index bd2498c..eb20fe5 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -21,20 +21,28 @@
 
 #include <chrono>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+#include <mach/mach_time.h>
+#endif
+
 namespace perfetto {
 namespace base {
 
 using TimeSeconds = std::chrono::seconds;
 using TimeMillis = std::chrono::milliseconds;
 using TimeNanos = std::chrono::nanoseconds;
-constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
 
 inline TimeNanos FromPosixTimespec(const struct timespec& ts) {
   return TimeNanos(ts.tv_sec * 1000000000LL + ts.tv_nsec);
 }
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+
+constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
+
 inline TimeNanos GetTimeInternalNs(clockid_t clk_id) {
   struct timespec ts = {};
   PERFETTO_CHECK(clock_gettime(clk_id, &ts) == 0);
@@ -45,6 +53,25 @@
   return GetTimeInternalNs(kWallTimeClockSource);
 }
 
+inline TimeNanos GetThreadCPUTimeNs() {
+  return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
+}
+
+#else  // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+
+inline TimeNanos GetWallTimeNs() {
+  auto init_time_factor = []() -> uint64_t {
+    mach_timebase_info_data_t timebase_info;
+    mach_timebase_info(&timebase_info);
+    return timebase_info.numer / timebase_info.denom;
+  };
+
+  static uint64_t monotonic_timebase_factor = init_time_factor();
+  return TimeNanos(mach_absolute_time() * monotonic_timebase_factor);
+}
+
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+
 inline TimeMillis GetWallTimeMs() {
   return std::chrono::duration_cast<TimeMillis>(GetWallTimeNs());
 }
@@ -53,10 +80,6 @@
   return std::chrono::duration_cast<TimeSeconds>(GetWallTimeNs());
 }
 
-inline TimeNanos GetThreadCPUTimeNs() {
-  return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
-}
-
 inline struct timespec ToPosixTimespec(TimeMillis time) {
   struct timespec ts {};
   const long time_s = static_cast<long>(time.count() / 1000);
diff --git a/include/perfetto/ipc/basic_types.h b/include/perfetto/ipc/basic_types.h
index 1d8612f..10c4dc7 100644
--- a/include/perfetto/ipc/basic_types.h
+++ b/include/perfetto/ipc/basic_types.h
@@ -17,6 +17,7 @@
 #ifndef INCLUDE_PERFETTO_IPC_BASIC_TYPES_H_
 #define INCLUDE_PERFETTO_IPC_BASIC_TYPES_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
 namespace google {
@@ -34,6 +35,10 @@
 using ClientID = uint64_t;
 using RequestID = uint64_t;
 
+// This determines the maximum size allowed for an IPC message. Trying to send
+// or receive a larger message will hit DCHECK(s) and auto-disconnect.
+constexpr size_t kIPCBufferSize = 128 * 1024;
+
 }  // namespace ipc
 }  // namespace perfetto
 
diff --git a/include/perfetto/protozero/BUILD.gn b/include/perfetto/protozero/BUILD.gn
index 7b91fe4..b97b137 100644
--- a/include/perfetto/protozero/BUILD.gn
+++ b/include/perfetto/protozero/BUILD.gn
@@ -21,6 +21,7 @@
     "message.h",
     "message_handle.h",
     "proto_field_descriptor.h",
+    "scattered_stream_null_delegate.h",
     "scattered_stream_writer.h",
   ]
 }
diff --git a/src/ftrace_reader/test/scattered_stream_null_delegate.h b/include/perfetto/protozero/scattered_stream_null_delegate.h
similarity index 61%
rename from src/ftrace_reader/test/scattered_stream_null_delegate.h
rename to include/perfetto/protozero/scattered_stream_null_delegate.h
index 41e2bd8..8efb477 100644
--- a/src/ftrace_reader/test/scattered_stream_null_delegate.h
+++ b/include/perfetto/protozero/scattered_stream_null_delegate.h
@@ -14,31 +14,32 @@
  * limitations under the License.
  */
 
-#ifndef SRC_FTRACE_READER_TEST_SCATTERED_STREAM_NULL_DELEGATE_H_
-#define SRC_FTRACE_READER_TEST_SCATTERED_STREAM_NULL_DELEGATE_H_
+#ifndef INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_NULL_DELEGATE_H_
+#define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_NULL_DELEGATE_H_
 
 #include <memory>
 #include <vector>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/protozero/contiguous_memory_range.h"
 #include "perfetto/protozero/scattered_stream_writer.h"
 
-namespace perfetto {
+namespace protozero {
 
-class ScatteredStreamNullDelegate
-    : public protozero::ScatteredStreamWriter::Delegate {
+class ScatteredStreamWriterNullDelegate
+    : public ScatteredStreamWriter::Delegate {
  public:
-  explicit ScatteredStreamNullDelegate(size_t chunk_size);
-  ~ScatteredStreamNullDelegate() override;
+  explicit ScatteredStreamWriterNullDelegate(size_t chunk_size);
+  ~ScatteredStreamWriterNullDelegate() override;
 
   // protozero::ScatteredStreamWriter::Delegate implementation.
-  protozero::ContiguousMemoryRange GetNewBuffer() override;
+  ContiguousMemoryRange GetNewBuffer() override;
 
  private:
   const size_t chunk_size_;
   std::unique_ptr<uint8_t[]> chunk_;
 };
 
-}  // namespace perfetto
+}  // namespace protozero
 
-#endif  // SRC_FTRACE_READER_TEST_SCATTERED_STREAM_NULL_DELEGATE_H_
+#endif  // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_NULL_DELEGATE_H_
diff --git a/include/perfetto/tracing/core/basic_types.h b/include/perfetto/tracing/core/basic_types.h
index c5a3020..112f58a 100644
--- a/include/perfetto/tracing/core/basic_types.h
+++ b/include/perfetto/tracing/core/basic_types.h
@@ -29,9 +29,6 @@
 using ProducerID = uint16_t;
 
 // Unique within the scope of the tracing service.
-using DataSourceID = uint64_t;
-
-// Unique within the scope of the tracing service.
 using DataSourceInstanceID = uint64_t;
 
 // Unique within the scope of a Producer.
diff --git a/include/perfetto/tracing/core/data_source_config.h b/include/perfetto/tracing/core/data_source_config.h
index 9b41045..619168b 100644
--- a/include/perfetto/tracing/core/data_source_config.h
+++ b/include/perfetto/tracing/core/data_source_config.h
@@ -37,6 +37,8 @@
 
 #include "perfetto/tracing/core/chrome_config.h"
 #include "perfetto/tracing/core/ftrace_config.h"
+#include "perfetto/tracing/core/inode_file_config.h"
+#include "perfetto/tracing/core/process_stats_config.h"
 #include "perfetto/tracing/core/test_config.h"
 
 // Forward declarations for protobuf types.
@@ -45,6 +47,9 @@
 class DataSourceConfig;
 class FtraceConfig;
 class ChromeConfig;
+class InodeFileConfig;
+class InodeFileConfig_MountPointMappingEntry;
+class ProcessStatsConfig;
 class TestConfig;
 }  // namespace protos
 }  // namespace perfetto
@@ -79,6 +84,21 @@
   const ChromeConfig& chrome_config() const { return chrome_config_; }
   ChromeConfig* mutable_chrome_config() { return &chrome_config_; }
 
+  const InodeFileConfig& inode_file_config() const {
+    return inode_file_config_;
+  }
+  InodeFileConfig* mutable_inode_file_config() { return &inode_file_config_; }
+
+  const ProcessStatsConfig& process_stats_config() const {
+    return process_stats_config_;
+  }
+  ProcessStatsConfig* mutable_process_stats_config() {
+    return &process_stats_config_;
+  }
+
+  const std::string& legacy_config() const { return legacy_config_; }
+  void set_legacy_config(const std::string& value) { legacy_config_ = value; }
+
   const TestConfig& for_testing() const { return for_testing_; }
   TestConfig* mutable_for_testing() { return &for_testing_; }
 
@@ -88,6 +108,9 @@
   uint32_t trace_duration_ms_ = {};
   FtraceConfig ftrace_config_ = {};
   ChromeConfig chrome_config_ = {};
+  InodeFileConfig inode_file_config_ = {};
+  ProcessStatsConfig process_stats_config_ = {};
+  std::string legacy_config_ = {};
   TestConfig for_testing_ = {};
 
   // Allows to preserve unknown protobuf fields for compatibility
diff --git a/include/perfetto/tracing/core/inode_file_config.h b/include/perfetto/tracing/core/inode_file_config.h
new file mode 100644
index 0000000..7704fcd
--- /dev/null
+++ b/include/perfetto/tracing/core/inode_file_config.h
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/inode_file/inode_file_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_INODE_FILE_CONFIG_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_INODE_FILE_CONFIG_H_
+
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+// Forward declarations for protobuf types.
+namespace perfetto {
+namespace protos {
+class InodeFileConfig;
+class InodeFileConfig_MountPointMappingEntry;
+}  // namespace protos
+}  // namespace perfetto
+
+namespace perfetto {
+
+class PERFETTO_EXPORT InodeFileConfig {
+ public:
+  class PERFETTO_EXPORT MountPointMappingEntry {
+   public:
+    MountPointMappingEntry();
+    ~MountPointMappingEntry();
+    MountPointMappingEntry(MountPointMappingEntry&&) noexcept;
+    MountPointMappingEntry& operator=(MountPointMappingEntry&&);
+    MountPointMappingEntry(const MountPointMappingEntry&);
+    MountPointMappingEntry& operator=(const MountPointMappingEntry&);
+
+    // Conversion methods from/to the corresponding protobuf types.
+    void FromProto(
+        const perfetto::protos::InodeFileConfig_MountPointMappingEntry&);
+    void ToProto(
+        perfetto::protos::InodeFileConfig_MountPointMappingEntry*) const;
+
+    const std::string& mountpoint() const { return mountpoint_; }
+    void set_mountpoint(const std::string& value) { mountpoint_ = value; }
+
+    int scan_roots_size() const { return static_cast<int>(scan_roots_.size()); }
+    const std::vector<std::string>& scan_roots() const { return scan_roots_; }
+    std::string* add_scan_roots() {
+      scan_roots_.emplace_back();
+      return &scan_roots_.back();
+    }
+
+   private:
+    std::string mountpoint_ = {};
+    std::vector<std::string> scan_roots_;
+
+    // Allows to preserve unknown protobuf fields for compatibility
+    // with future versions of .proto files.
+    std::string unknown_fields_;
+  };
+
+  InodeFileConfig();
+  ~InodeFileConfig();
+  InodeFileConfig(InodeFileConfig&&) noexcept;
+  InodeFileConfig& operator=(InodeFileConfig&&);
+  InodeFileConfig(const InodeFileConfig&);
+  InodeFileConfig& operator=(const InodeFileConfig&);
+
+  // Conversion methods from/to the corresponding protobuf types.
+  void FromProto(const perfetto::protos::InodeFileConfig&);
+  void ToProto(perfetto::protos::InodeFileConfig*) const;
+
+  uint64_t scan_interval_ms() const { return scan_interval_ms_; }
+  void set_scan_interval_ms(uint64_t value) { scan_interval_ms_ = value; }
+
+  uint64_t scan_delay_ms() const { return scan_delay_ms_; }
+  void set_scan_delay_ms(uint64_t value) { scan_delay_ms_ = value; }
+
+  uint64_t scan_batch_size() const { return scan_batch_size_; }
+  void set_scan_batch_size(uint64_t value) { scan_batch_size_ = value; }
+
+  bool do_not_scan() const { return do_not_scan_; }
+  void set_do_not_scan(bool value) { do_not_scan_ = value; }
+
+  int scan_mount_points_size() const {
+    return static_cast<int>(scan_mount_points_.size());
+  }
+  const std::vector<std::string>& scan_mount_points() const {
+    return scan_mount_points_;
+  }
+  std::string* add_scan_mount_points() {
+    scan_mount_points_.emplace_back();
+    return &scan_mount_points_.back();
+  }
+
+  int mount_point_mapping_size() const {
+    return static_cast<int>(mount_point_mapping_.size());
+  }
+  const std::vector<MountPointMappingEntry>& mount_point_mapping() const {
+    return mount_point_mapping_;
+  }
+  MountPointMappingEntry* add_mount_point_mapping() {
+    mount_point_mapping_.emplace_back();
+    return &mount_point_mapping_.back();
+  }
+
+ private:
+  uint64_t scan_interval_ms_ = {};
+  uint64_t scan_delay_ms_ = {};
+  uint64_t scan_batch_size_ = {};
+  bool do_not_scan_ = {};
+  std::vector<std::string> scan_mount_points_;
+  std::vector<MountPointMappingEntry> mount_point_mapping_;
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+};
+
+}  // namespace perfetto
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_INODE_FILE_CONFIG_H_
diff --git a/include/perfetto/tracing/core/process_stats_config.h b/include/perfetto/tracing/core/process_stats_config.h
new file mode 100644
index 0000000..2601ca0
--- /dev/null
+++ b/include/perfetto/tracing/core/process_stats_config.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/process_stats/process_stats_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_PROCESS_STATS_CONFIG_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_PROCESS_STATS_CONFIG_H_
+
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+// Forward declarations for protobuf types.
+namespace perfetto {
+namespace protos {
+class ProcessStatsConfig;
+}
+}  // namespace perfetto
+
+namespace perfetto {
+
+class PERFETTO_EXPORT ProcessStatsConfig {
+ public:
+  enum Quirks {
+    QUIRKS_UNSPECIFIED = 0,
+    DISABLE_INITIAL_DUMP = 1,
+    DISABLE_ON_DEMAND = 2,
+  };
+  ProcessStatsConfig();
+  ~ProcessStatsConfig();
+  ProcessStatsConfig(ProcessStatsConfig&&) noexcept;
+  ProcessStatsConfig& operator=(ProcessStatsConfig&&);
+  ProcessStatsConfig(const ProcessStatsConfig&);
+  ProcessStatsConfig& operator=(const ProcessStatsConfig&);
+
+  // Conversion methods from/to the corresponding protobuf types.
+  void FromProto(const perfetto::protos::ProcessStatsConfig&);
+  void ToProto(perfetto::protos::ProcessStatsConfig*) const;
+
+  int quirks_size() const { return static_cast<int>(quirks_.size()); }
+  const std::vector<Quirks>& quirks() const { return quirks_; }
+  Quirks* add_quirks() {
+    quirks_.emplace_back();
+    return &quirks_.back();
+  }
+
+ private:
+  std::vector<Quirks> quirks_;
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+};
+
+}  // namespace perfetto
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_PROCESS_STATS_CONFIG_H_
diff --git a/include/perfetto/tracing/core/service.h b/include/perfetto/tracing/core/service.h
index ed810ec..250308e 100644
--- a/include/perfetto/tracing/core/service.h
+++ b/include/perfetto/tracing/core/service.h
@@ -66,14 +66,10 @@
    public:
     virtual ~ProducerEndpoint() = default;
 
-    // Called by the Producer to (un)register data sources. The Services returns
-    // asynchronousy the ID for the data source.
-    // TODO(primiano): thinking twice there is no reason why the service choses
-    // ID rather than the Producer. Update in upcoming CLs.
-    using RegisterDataSourceCallback = std::function<void(DataSourceID)>;
-    virtual void RegisterDataSource(const DataSourceDescriptor&,
-                                    RegisterDataSourceCallback) = 0;
-    virtual void UnregisterDataSource(DataSourceID) = 0;
+    // Called by the Producer to (un)register data sources. Data sources are
+    // identified by their name (i.e. DataSourceDescriptor.name)
+    virtual void RegisterDataSource(const DataSourceDescriptor&) = 0;
+    virtual void UnregisterDataSource(const std::string& name) = 0;
 
     // Called by the Producer to signal that some pages in the shared memory
     // buffer (shared between Service and Producer) have changed.
diff --git a/include/perfetto/tracing/core/shared_memory_abi.h b/include/perfetto/tracing/core/shared_memory_abi.h
index 4d85f1b..303deff 100644
--- a/include/perfetto/tracing/core/shared_memory_abi.h
+++ b/include/perfetto/tracing/core/shared_memory_abi.h
@@ -142,6 +142,9 @@
 
 class SharedMemoryABI {
  public:
+  // This is due to Chunk::size being 16 bits.
+  static constexpr size_t kMaxPageSize = 64 * 1024;
+
   // "14" is the max number that can be encoded in a 32 bit atomic word using
   // 2 state bits per Chunk and leaving 4 bits for the page layout.
   // See PageLayout below.
diff --git a/include/perfetto/tracing/core/shared_memory_arbiter.h b/include/perfetto/tracing/core/shared_memory_arbiter.h
index 9efbba8..2cd336c 100644
--- a/include/perfetto/tracing/core/shared_memory_arbiter.h
+++ b/include/perfetto/tracing/core/shared_memory_arbiter.h
@@ -46,8 +46,7 @@
   // Creates a new TraceWriter and assigns it a new WriterID. The WriterID is
   // written in each chunk header owned by a given TraceWriter and is used by
   // the Service to reconstruct TracePackets written by the same TraceWriter.
-  // Returns nullptr if all WriterID slots are exhausted.
-  // TODO(primiano): instead of nullptr this should return a NoopWriter.
+  // Returns null impl of TraceWriter if all WriterID slots are exhausted.
   virtual std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer) = 0;
 
diff --git a/include/perfetto/tracing/core/slice.h b/include/perfetto/tracing/core/slice.h
index f845ebc..954e58d 100644
--- a/include/perfetto/tracing/core/slice.h
+++ b/include/perfetto/tracing/core/slice.h
@@ -21,6 +21,7 @@
 #include <string.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "perfetto/base/logging.h"
@@ -32,6 +33,11 @@
 struct Slice {
   Slice() : start(nullptr), size(0) {}
   Slice(const void* st, size_t sz) : start(st), size(sz) {}
+
+  // Used to inherit ownership of a buffer from a protobuf via release_str().
+  explicit Slice(std::unique_ptr<std::string> str)
+      : start(&(*str)[0]), size(str->size()), moved_str_data_(std::move(str)) {}
+
   Slice(Slice&& other) noexcept = default;
 
   // Create a Slice which owns |size| bytes of memory.
@@ -56,6 +62,7 @@
   void operator=(const Slice&) = delete;
 
   std::unique_ptr<uint8_t[]> own_data_;
+  std::unique_ptr<std::string> moved_str_data_;
 };
 
 // TODO(primiano): most TracePacket(s) fit in a slice or two. We need something
diff --git a/include/perfetto/tracing/core/test_config.h b/include/perfetto/tracing/core/test_config.h
index 48f4629..95af155 100644
--- a/include/perfetto/tracing/core/test_config.h
+++ b/include/perfetto/tracing/core/test_config.h
@@ -60,6 +60,11 @@
   uint32_t message_count() const { return message_count_; }
   void set_message_count(uint32_t value) { message_count_ = value; }
 
+  uint32_t max_messages_per_second() const { return max_messages_per_second_; }
+  void set_max_messages_per_second(uint32_t value) {
+    max_messages_per_second_ = value;
+  }
+
   uint32_t seed() const { return seed_; }
   void set_seed(uint32_t value) { seed_ = value; }
 
@@ -68,6 +73,7 @@
 
  private:
   uint32_t message_count_ = {};
+  uint32_t max_messages_per_second_ = {};
   uint32_t seed_ = {};
   uint64_t message_size_ = {};
 
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index 2d5bde6..2097906 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -46,6 +46,9 @@
 class DataSourceConfig;
 class FtraceConfig;
 class ChromeConfig;
+class InodeFileConfig;
+class InodeFileConfig_MountPointMappingEntry;
+class ProcessStatsConfig;
 class TestConfig;
 class TraceConfig_ProducerConfig;
 class TraceConfig_StatsdMetadata;
diff --git a/include/perfetto/tracing/core/trace_packet.h b/include/perfetto/tracing/core/trace_packet.h
index 14c4e0d..0536d70 100644
--- a/include/perfetto/tracing/core/trace_packet.h
+++ b/include/perfetto/tracing/core/trace_packet.h
@@ -88,6 +88,10 @@
   size_t size_ = 0;   // SUM(slice.size for slice in slices_).
   char preamble_[8];  // Deliberately not initialized.
   std::unique_ptr<DecodedTracePacket> decoded_packet_;
+
+  // Remember to update the move operators and their unittest if adding new
+  // fields. ConsumerIPCClientImpl::OnReadBuffersResponse() relies on
+  // std::move(TracePacket) to clear up the moved-from instance.
 };
 
 }  // namespace perfetto
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 77dc8613..74fa6c1 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -25,6 +25,8 @@
     "data_source_config.proto",
     "data_source_descriptor.proto",
     "ftrace/ftrace_config.proto",
+    "inode_file/inode_file_config.proto",
+    "process_stats/process_stats_config.proto",
     "test_config.proto",
     "trace_config.proto",
   ]
@@ -38,6 +40,8 @@
     "data_source_config.proto",
     "data_source_descriptor.proto",
     "ftrace/ftrace_config.proto",
+    "inode_file/inode_file_config.proto",
+    "process_stats/process_stats_config.proto",
     "test_config.proto",
     "trace_config.proto",
   ]
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index 3c33a10..c7b2f21 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -21,6 +21,8 @@
 
 import "perfetto/config/chrome/chrome_config.proto";
 import "perfetto/config/ftrace/ftrace_config.proto";
+import "perfetto/config/inode_file/inode_file_config.proto";
+import "perfetto/config/process_stats/process_stats_config.proto";
 import "perfetto/config/test_config.proto";
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
 // to reflect changes in the corresponding C++ headers.
@@ -49,6 +51,17 @@
 
   optional FtraceConfig ftrace_config = 100;
   optional ChromeConfig chrome_config = 101;
+  optional InodeFileConfig inode_file_config = 102;
+  optional ProcessStatsConfig process_stats_config = 103;
+
+  // This is a fallback mechanism to send a free-form text config to the
+  // producer. In theory this should never be needed. All the code that
+  // is part of the platform (i.e. traced service) is supposed to *not* truncate
+  // the trace config proto and propagate unknown fields. However, if anything
+  // in the pipeline (client or backend) ends up breaking this forward compat
+  // plan, this field will become the escape hatch to allow future data sources
+  // to get some meaningful configuration.
+  optional string legacy_config = 1000;
 
   // This field is only used for testing.
   optional TestConfig for_testing =
diff --git a/protos/perfetto/config/inode_file/inode_file_config.proto b/protos/perfetto/config/inode_file/inode_file_config.proto
new file mode 100644
index 0000000..a1127ff
--- /dev/null
+++ b/protos/perfetto/config/inode_file/inode_file_config.proto
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+message InodeFileConfig {
+  message MountPointMappingEntry {
+    optional string mountpoint = 1;
+    repeated string scan_roots = 2;
+  }
+
+  // How long to pause between batches.
+  optional uint64 scan_interval_ms = 1;
+
+  // How long to wait before the first scan in order to accumulate inodes.
+  optional uint64 scan_delay_ms = 2;
+
+  // How many inodes to scan in one batch.
+  optional uint64 scan_batch_size = 3;
+
+  // Do not scan for inodes not found in the static map.
+  optional bool do_not_scan = 4;
+
+  // If non-empty, only scan inodes corresponding to block devices named in
+  // this list.
+  repeated string scan_mount_points = 5;
+
+  // When encountering an inode belonging to a block device corresponding
+  // to one of the mount points in this map, scan its scan_roots instead.
+  repeated MountPointMappingEntry mount_point_mapping = 6;
+}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index e149b49..4efae27 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -22,6 +22,58 @@
 
 // End of protos/perfetto/config/chrome/chrome_config.proto
 
+// Begin of protos/perfetto/config/inode_file/inode_file_config.proto
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+message InodeFileConfig {
+  message MountPointMappingEntry {
+    optional string mountpoint = 1;
+    repeated string scan_roots = 2;
+  }
+
+  // How long to pause between batches.
+  optional uint64 scan_interval_ms = 1;
+
+  // How long to wait before the first scan in order to accumulate inodes.
+  optional uint64 scan_delay_ms = 2;
+
+  // How many inodes to scan in one batch.
+  optional uint64 scan_batch_size = 3;
+
+  // Do not scan for inodes not found in the static map.
+  optional bool do_not_scan = 4;
+
+  // If non-empty, only scan inodes corresponding to block devices named in
+  // this list.
+  repeated string scan_mount_points = 5;
+
+  // When encountering an inode belonging to a block device corresponding
+  // to one of the mount points in this map, scan its scan_roots instead.
+  repeated MountPointMappingEntry mount_point_mapping = 6;
+}
+
+// End of protos/perfetto/config/inode_file/inode_file_config.proto
+
+// Begin of protos/perfetto/config/process_stats/process_stats_config.proto
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+message ProcessStatsConfig {
+  enum Quirks {
+    QUIRKS_UNSPECIFIED = 0;
+    DISABLE_INITIAL_DUMP = 1;
+    // TODO(taylori) Not implemented.
+    DISABLE_ON_DEMAND = 2;
+  }
+
+  repeated Quirks quirks = 1;
+}
+
+// End of protos/perfetto/config/process_stats/process_stats_config.proto
+
 // Begin of protos/perfetto/config/data_source_config.proto
 
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
@@ -51,6 +103,17 @@
 
   optional FtraceConfig ftrace_config = 100;
   optional ChromeConfig chrome_config = 101;
+  optional InodeFileConfig inode_file_config = 102;
+  optional ProcessStatsConfig process_stats_config = 103;
+
+  // This is a fallback mechanism to send a free-form text config to the
+  // producer. In theory this should never be needed. All the code that
+  // is part of the platform (i.e. traced service) is supposed to *not* truncate
+  // the trace config proto and propagate unknown fields. However, if anything
+  // in the pipeline (client or backend) ends up breaking this forward compat
+  // plan, this field will become the escape hatch to allow future data sources
+  // to get some meaningful configuration.
+  optional string legacy_config = 1000;
 
   // This field is only used for testing.
   optional TestConfig for_testing =
@@ -85,14 +148,20 @@
   // The number of messages the fake producer should send.
   optional uint32 message_count = 1;
 
+  // The maximum number of messages which should be sent each second.
+  // The actual obserced speed may be lower if the producer is unable to
+  // work fast enough.
+  // If this is zero or unset, the producer will send as fast as possible.
+  optional uint32 max_messages_per_second = 2;
+
   // The seed value for a simple multiplicative congruential pseudo-random
   // number sequence.
-  optional uint32 seed = 2;
+  optional uint32 seed = 3;
 
   // The size of each message in bytes. Should be greater than or equal 5 to
   // account for the number of bytes needed to encode the random number and a
   // null byte for the string.
-  optional uint64 message_size = 3;
+  optional uint64 message_size = 4;
 }
 
 // End of protos/perfetto/config/test_config.proto
@@ -115,7 +184,6 @@
     reserved 2;  // |page_size|, now deprecated.
     reserved 3;  // |optimize_for|, now deprecated.
 
-    // Keep include/perfetto/tracing/core/trace_config.h in sync.
     enum FillPolicy {
       UNSPECIFIED = 0;
       RING_BUFFER = 1;
diff --git a/protos/perfetto/config/process_stats/process_stats_config.proto b/protos/perfetto/config/process_stats/process_stats_config.proto
new file mode 100644
index 0000000..bce97e7
--- /dev/null
+++ b/protos/perfetto/config/process_stats/process_stats_config.proto
@@ -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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+message ProcessStatsConfig {
+  enum Quirks {
+    QUIRKS_UNSPECIFIED = 0;
+    DISABLE_INITIAL_DUMP = 1;
+    // TODO(taylori) Not implemented.
+    DISABLE_ON_DEMAND = 2;
+  }
+
+  repeated Quirks quirks = 1;
+}
diff --git a/protos/perfetto/config/test_config.proto b/protos/perfetto/config/test_config.proto
index b679235..8f0045d 100644
--- a/protos/perfetto/config/test_config.proto
+++ b/protos/perfetto/config/test_config.proto
@@ -27,12 +27,18 @@
   // The number of messages the fake producer should send.
   optional uint32 message_count = 1;
 
+  // The maximum number of messages which should be sent each second.
+  // The actual obserced speed may be lower if the producer is unable to
+  // work fast enough.
+  // If this is zero or unset, the producer will send as fast as possible.
+  optional uint32 max_messages_per_second = 2;
+
   // The seed value for a simple multiplicative congruential pseudo-random
   // number sequence.
-  optional uint32 seed = 2;
+  optional uint32 seed = 3;
 
   // The size of each message in bytes. Should be greater than or equal 5 to
   // account for the number of bytes needed to encode the random number and a
   // null byte for the string.
-  optional uint64 message_size = 3;
+  optional uint64 message_size = 4;
 }
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index c5d057a..36a02bb 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -37,7 +37,6 @@
     reserved 2;  // |page_size|, now deprecated.
     reserved 3;  // |optimize_for|, now deprecated.
 
-    // Keep include/perfetto/tracing/core/trace_config.h in sync.
     enum FillPolicy {
       UNSPECIFIED = 0;
       RING_BUFFER = 1;
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index abad343..d9b1db5 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -77,22 +77,21 @@
 message ReadBuffersResponse {
   // TODO: uint32 buffer_id = 1;
 
-  // Each streaming reply returns one or more trace packets (see
-  // trace_packet.proto).
-  // Why "bytes" below? If we just return the full TracePacket object, that will
-  // force the Consumer to deserialize it. In many occasions, the Consumer will
-  // not consume the TracePacket(s) locally but will just forward them over
-  // the network or save them to a file. Deserializing them on-device would be
-  // a waste of time, memory and energy.
+  // Each streaming reply returns one or more slices for one or more trace
+  // packets, or even just a portion of it (if it's too big to fit within one
+  // IPC). The returned slices are ordered and contiguous: packets' slices are
+  // not interleaved and slices are sent only once all slices for a packet are
+  // available (i.e. the consumer will never see any gap).
+  message Slice {
+    optional bytes data = 1;
 
-  // TODO: in the past we agreed that a TracePacket can be very large (MBs).
-  // However here it will hit the limit of the IPC layer in order to keep
-  // the socket buffer bounded. On one side we could upgrade this protocol to
-  // support chunks, so we could directly propagate the chunked TracePacket
-  // stored in the log buffer. On the other side, this will likely just move
-  // the problem on the consumer, that will need larger buffers for reassembly.
-  // Perhaps we should just cap the size of a TracePacket to a lower size?
-  repeated bytes trace_packets = 2;
+    // When true, this is the last slice for the packet. A ReadBufferResponse
+    // might have no slices marked as |last_slice_for_packet|==true, in the case
+    // of a very large packet that gets chunked into several IPCs (in which case
+    // only the last IPC for the packet will have this flag set).
+    optional bool last_slice_for_packet = 2;
+  }
+  repeated Slice slices = 2;
 }
 
 // Arguments for rpc FreeBuffers().
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index 4665071..f151fd5 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -76,19 +76,16 @@
 }
 
 message RegisterDataSourceResponse {
-  // The ID assigned by the service to this data source. 0 in case of errors.
-  optional uint64 data_source_id = 1;
-
   // Only set in case of errors, when |data_source_id| == 0.
-  optional string error = 2;
+  optional string error = 1;
 };
 
 // Arguments for rpc UnregisterDataSource().
 
 message UnregisterDataSourceRequest {
-  // The ID of the data source to unregister, as previously returned in
-  // |RegisterDataSourceResponse.data_source_id|.
-  optional uint64 data_source_id = 1;
+  // The name of the data source to unregister, as previously passed in
+  // |RegisterDataSourceRequest.name|.
+  optional string data_source_name = 1;
 }
 
 message UnregisterDataSourceResponse {}
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index 7e2a6a6..46de5df 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -35,8 +35,6 @@
     "test/cpu_reader_support.h",
     "test/scattered_stream_delegate_for_testing.cc",
     "test/scattered_stream_delegate_for_testing.h",
-    "test/scattered_stream_null_delegate.cc",
-    "test/scattered_stream_null_delegate.h",
   ]
 }
 
diff --git a/src/ftrace_reader/cpu_reader_benchmark.cc b/src/ftrace_reader/cpu_reader_benchmark.cc
index 8771bac..80ca016 100644
--- a/src/ftrace_reader/cpu_reader_benchmark.cc
+++ b/src/ftrace_reader/cpu_reader_benchmark.cc
@@ -16,16 +16,15 @@
 
 #include "src/ftrace_reader/cpu_reader.h"
 
+#include "perfetto/base/utils.h"
+#include "perfetto/protozero/scattered_stream_null_delegate.h"
 #include "perfetto/protozero/scattered_stream_writer.h"
 
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "test/cpu_reader_support.h"
-#include "test/scattered_stream_null_delegate.h"
 
 namespace {
 
-constexpr size_t kPageSize = 4096;
-
 perfetto::ExamplePage g_full_page_sched_switch{
     "synthetic",
     R"(
@@ -293,7 +292,8 @@
 using perfetto::ExamplePage;
 using perfetto::EventFilter;
 using perfetto::ProtoTranslationTable;
-using perfetto::ScatteredStreamNullDelegate;
+using protozero::ScatteredStreamWriterNullDelegate;
+using protozero::ScatteredStreamWriter;
 using perfetto::GetTable;
 using perfetto::PageFromXxd;
 using perfetto::protos::pbzero::FtraceEventBundle;
@@ -303,8 +303,8 @@
 static void BM_ParsePageFullOfSchedSwitch(benchmark::State& state) {
   const ExamplePage* test_case = &g_full_page_sched_switch;
 
-  ScatteredStreamNullDelegate delegate(kPageSize);
-  protozero::ScatteredStreamWriter stream(&delegate);
+  ScatteredStreamWriterNullDelegate delegate(perfetto::base::kPageSize);
+  ScatteredStreamWriter stream(&delegate);
   FtraceEventBundle writer;
 
   ProtoTranslationTable* table = GetTable(test_case->name);
diff --git a/src/ftrace_reader/cpu_reader_fuzzer.cc b/src/ftrace_reader/cpu_reader_fuzzer.cc
index c41b528..e1ebae1 100644
--- a/src/ftrace_reader/cpu_reader_fuzzer.cc
+++ b/src/ftrace_reader/cpu_reader_fuzzer.cc
@@ -21,11 +21,11 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/utils.h"
+#include "perfetto/protozero/scattered_stream_null_delegate.h"
 #include "perfetto/protozero/scattered_stream_writer.h"
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "src/ftrace_reader/cpu_reader.h"
 #include "test/cpu_reader_support.h"
-#include "test/scattered_stream_null_delegate.h"
 
 namespace perfetto {
 namespace {
@@ -39,7 +39,7 @@
 void FuzzCpuReaderParsePage(const uint8_t* data, size_t size);
 
 void FuzzCpuReaderParsePage(const uint8_t* data, size_t size) {
-  ScatteredStreamNullDelegate delegate(base::kPageSize);
+  protozero::ScatteredStreamWriterNullDelegate delegate(base::kPageSize);
   protozero::ScatteredStreamWriter stream(&delegate);
   FtraceEventBundle writer;
 
diff --git a/src/ftrace_reader/ftrace_config_muxer.cc b/src/ftrace_reader/ftrace_config_muxer.cc
index 641bd28..2f221b7 100644
--- a/src/ftrace_reader/ftrace_config_muxer.cc
+++ b/src/ftrace_reader/ftrace_config_muxer.cc
@@ -236,8 +236,6 @@
       return 0;
 
     // If we're about to turn tracing on use this opportunity do some setup:
-    if (RequiresAtrace(request))
-      EnableAtrace(request);
     SetupClock(request);
     SetupBufferSize(request);
   } else {
@@ -248,6 +246,9 @@
 
   std::set<std::string> events = GetFtraceEvents(request, table_);
 
+  if (RequiresAtrace(request))
+    UpdateAtrace(request);
+
   for (auto& name : events) {
     const Event* event = table_->GetEventByName(name);
     if (!event) {
@@ -341,14 +342,13 @@
   current_state_.cpu_buffer_size_pages = pages;
 }
 
-void FtraceConfigMuxer::EnableAtrace(const FtraceConfig& request) {
-  PERFETTO_DCHECK(!current_state_.atrace_on);
-
-  PERFETTO_DLOG("Start atrace...");
+void FtraceConfigMuxer::UpdateAtrace(const FtraceConfig& request) {
+  PERFETTO_DLOG("Update atrace config...");
 
   std::vector<std::string> args;
   args.push_back("atrace");  // argv0 for exec()
   args.push_back("--async_start");
+  args.push_back("--only_userspace");
   for (const auto& category : request.atrace_categories())
     args.push_back(category);
   if (!request.atrace_apps().empty()) {
@@ -368,7 +368,7 @@
 
   PERFETTO_DLOG("Stop atrace...");
 
-  if (RunAtrace({"atrace", "--async_stop"}))
+  if (RunAtrace({"atrace", "--async_stop", "--only_userspace"}))
     current_state_.atrace_on = false;
 
   PERFETTO_DLOG("...done");
diff --git a/src/ftrace_reader/ftrace_config_muxer.h b/src/ftrace_reader/ftrace_config_muxer.h
index de2a7b0..82f51c4 100644
--- a/src/ftrace_reader/ftrace_config_muxer.h
+++ b/src/ftrace_reader/ftrace_config_muxer.h
@@ -80,7 +80,7 @@
 
   void SetupClock(const FtraceConfig& request);
   void SetupBufferSize(const FtraceConfig& request);
-  void EnableAtrace(const FtraceConfig& request);
+  void UpdateAtrace(const FtraceConfig& request);
   void DisableAtrace();
 
   FtraceConfigId GetNextId();
diff --git a/src/ftrace_reader/ftrace_config_muxer_unittest.cc b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
index e3be7aa..1833fbd 100644
--- a/src/ftrace_reader/ftrace_config_muxer_unittest.cc
+++ b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
@@ -212,7 +212,8 @@
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
   EXPECT_CALL(atrace,
-              RunAtrace(ElementsAreArray({"atrace", "--async_start", "sched"})))
+              RunAtrace(ElementsAreArray(
+                  {"atrace", "--async_start", "--only_userspace", "sched"})))
       .WillOnce(Return(true));
 
   FtraceConfigId id = model.RequestConfig(config);
@@ -223,7 +224,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"})))
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
+                          {"atrace", "--async_stop", "--only_userspace"})))
       .WillOnce(Return(true));
   ASSERT_TRUE(model.RemoveConfig(id));
 }
diff --git a/src/ftrace_reader/ftrace_controller.cc b/src/ftrace_reader/ftrace_controller.cc
index ba2d4ec..ee322ec 100644
--- a/src/ftrace_reader/ftrace_controller.cc
+++ b/src/ftrace_reader/ftrace_controller.cc
@@ -307,9 +307,7 @@
   if (cpus_to_drain_.none()) {
     // If this was the first CPU to wake up, schedule a drain for the next drain
     // interval.
-    uint64_t delay_ms = NowMs() % drain_period_ms;
-    if (!delay_ms)
-      delay_ms = drain_period_ms;
+    uint64_t delay_ms = drain_period_ms - (NowMs() % drain_period_ms);
     task_runner_->PostDelayedTask(
         std::bind(&FtraceController::DrainCPUs, weak_this, generation),
         static_cast<int>(delay_ms));
diff --git a/src/ftrace_reader/test/cpu_reader_support.cc b/src/ftrace_reader/test/cpu_reader_support.cc
index 7554346..0a778ca 100644
--- a/src/ftrace_reader/test/cpu_reader_support.cc
+++ b/src/ftrace_reader/test/cpu_reader_support.cc
@@ -16,6 +16,7 @@
 
 #include "src/ftrace_reader/test/cpu_reader_support.h"
 
+#include "perfetto/base/utils.h"
 #include "src/ftrace_reader/ftrace_procfs.h"
 
 #include <string.h>
@@ -23,8 +24,6 @@
 namespace perfetto {
 namespace {
 
-constexpr size_t kPageSize = 4096;
-
 std::map<std::string, std::unique_ptr<ProtoTranslationTable>>* g_tables;
 
 }  // namespace
@@ -44,9 +43,9 @@
 }
 
 std::unique_ptr<uint8_t[]> PageFromXxd(const std::string& text) {
-  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[kPageSize]);
+  auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[base::kPageSize]);
   const char* ptr = text.data();
-  memset(buffer.get(), 0xfa, kPageSize);
+  memset(buffer.get(), 0xfa, base::kPageSize);
   uint8_t* out = buffer.get();
   while (*ptr != '\0') {
     if (*(ptr++) != ':')
diff --git a/src/ipc/buffered_frame_deserializer.cc b/src/ipc/buffered_frame_deserializer.cc
index 9665398..472b302 100644
--- a/src/ipc/buffered_frame_deserializer.cc
+++ b/src/ipc/buffered_frame_deserializer.cc
@@ -184,6 +184,8 @@
   frame.AppendToString(&buf);
   const uint32_t payload_size = static_cast<uint32_t>(buf.size() - kHeaderSize);
   PERFETTO_DCHECK(payload_size == static_cast<uint32_t>(frame.GetCachedSize()));
+  // Don't send messages larger than what the receiver can handle.
+  PERFETTO_DCHECK(kHeaderSize + payload_size <= kIPCBufferSize);
   char header[kHeaderSize];
   memcpy(header, base::AssumeLittleEndian(&payload_size), kHeaderSize);
   buf.replace(0, kHeaderSize, header, kHeaderSize);
diff --git a/src/ipc/buffered_frame_deserializer.h b/src/ipc/buffered_frame_deserializer.h
index 40e1a64..52a5f5f 100644
--- a/src/ipc/buffered_frame_deserializer.h
+++ b/src/ipc/buffered_frame_deserializer.h
@@ -25,6 +25,7 @@
 #include <sys/mman.h>
 
 #include "perfetto/base/page_allocator.h"
+#include "perfetto/ipc/basic_types.h"
 
 namespace perfetto {
 namespace ipc {
@@ -81,7 +82,8 @@
     size_t size;
   };
 
-  explicit BufferedFrameDeserializer(size_t max_capacity = 128 * 1024);
+  // |max_capacity| is overridable only for tests.
+  explicit BufferedFrameDeserializer(size_t max_capacity = kIPCBufferSize);
   ~BufferedFrameDeserializer();
 
   // This function doesn't really belong here as it does Serialization, unlike
diff --git a/src/ipc/unix_socket.cc b/src/ipc/unix_socket.cc
index 463682b..fa5fc2e 100644
--- a/src/ipc/unix_socket.cc
+++ b/src/ipc/unix_socket.cc
@@ -351,15 +351,16 @@
   if (blocking_mode == BlockingMode::kBlocking)
     SetBlockingIO(false);
 
-  if (sz >= 0) {
-    // There should be no way a non-blocking socket returns < |len|.
-    // If the queueing fails, sendmsg() must return -1 + errno = EWOULDBLOCK.
-    PERFETTO_CHECK(static_cast<size_t>(sz) == len);
+  if (sz == static_cast<ssize_t>(len)) {
     last_error_ = 0;
     return true;
   }
 
-  if (errno == EAGAIN || errno == EWOULDBLOCK) {
+  // If sendmsg() succeds but the returned size is < |len| it means that the
+  // endpoint disconnected in the middle of the read, and we managed to send
+  // only a portion of the buffer. In this case we should just give up.
+
+  if (sz < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
     // A genuine out-of-buffer. The client should retry or give up.
     // Man pages specify that EAGAIN and EWOULDBLOCK have the same semantic here
     // and clients should check for both.
diff --git a/src/ipc/unix_socket_unittest.cc b/src/ipc/unix_socket_unittest.cc
index 0c07094..efc8063 100644
--- a/src/ipc/unix_socket_unittest.cc
+++ b/src/ipc/unix_socket_unittest.cc
@@ -478,6 +478,55 @@
   tx_thread.join();
 }
 
+// Regression test for b/76155349 . If the receiver end disconnects while the
+// sender is in the middle of a large send(), the socket should gracefully give
+// up (i.e. Shutdown()) but not crash.
+TEST_F(UnixSocketTest, ReceiverDisconnectsDuringSend) {
+  auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
+  ASSERT_TRUE(srv->is_listening());
+  const int kTimeoutMs = 30000;
+
+  auto receive_done = task_runner_.CreateCheckpoint("receive_done");
+  EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
+      .WillOnce(Invoke([this, receive_done](UnixSocket*, UnixSocket* srv_conn) {
+        EXPECT_CALL(event_listener_, OnDataAvailable(srv_conn))
+            .WillOnce(Invoke([receive_done](UnixSocket* s) {
+              char buf[1024];
+              size_t res = s->Receive(buf, sizeof(buf));
+              ASSERT_EQ(1024u, res);
+              s->Shutdown(false /*notify*/);
+              receive_done();
+            }));
+      }));
+
+  // Perform the blocking send form another thread.
+  std::thread tx_thread([] {
+    base::TestTaskRunner tx_task_runner;
+    MockEventListener tx_events;
+    auto cli = UnixSocket::Connect(kSocketName, &tx_events, &tx_task_runner);
+
+    auto cli_connected = tx_task_runner.CreateCheckpoint("cli_connected");
+    EXPECT_CALL(tx_events, OnConnect(cli.get(), true))
+        .WillOnce(InvokeWithoutArgs(cli_connected));
+    tx_task_runner.RunUntilCheckpoint("cli_connected");
+
+    auto send_done = tx_task_runner.CreateCheckpoint("send_done");
+    // We need a
+    static constexpr size_t kBufSize = 32 * 1024 * 1024;
+    std::unique_ptr<char[]> buf(new char[kBufSize]());
+    tx_task_runner.PostTask([&cli, &buf, send_done] {
+      bool send_res = cli->Send(buf.get(), kBufSize, -1 /*fd*/,
+                                UnixSocket::BlockingMode::kBlocking);
+      ASSERT_FALSE(send_res);
+      send_done();
+    });
+
+    tx_task_runner.RunUntilCheckpoint("send_done", kTimeoutMs);
+  });
+  task_runner_.RunUntilCheckpoint("receive_done", kTimeoutMs);
+  tx_thread.join();
+}
+
 // TODO(primiano): add a test to check that in the case of a peer sending a fd
 // and the other end just doing a recv (without taking it), the fd is closed and
 // not left around.
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 144b008..5a86919 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -130,7 +130,7 @@
         test_config.add_buffers()->set_size_kb(4096);
         test_config.set_duration_ms(2000);
         auto* ds_config = test_config.add_data_sources()->mutable_config();
-        ds_config->set_name("com.google.perfetto.ftrace");
+        ds_config->set_name("linux.ftrace");
         ds_config->mutable_ftrace_config()->add_ftrace_events("sched_switch");
         ds_config->mutable_ftrace_config()->add_ftrace_events("cpu_idle");
         ds_config->mutable_ftrace_config()->add_ftrace_events("cpu_frequency");
@@ -275,14 +275,14 @@
 
 void PerfettoCmd::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
   for (TracePacket& packet : packets) {
+    uint8_t preamble[16];
+    uint8_t* pos = preamble;
+    pos = WriteVarInt(MakeTagLengthDelimited(protos::Trace::kPacketFieldNumber),
+                      pos);
+    pos = WriteVarInt(static_cast<uint32_t>(packet.size()), pos);
+    fwrite(reinterpret_cast<const char*>(preamble), pos - preamble, 1,
+           trace_out_stream_.get());
     for (const Slice& slice : packet.slices()) {
-      uint8_t preamble[16];
-      uint8_t* pos = preamble;
-      pos = WriteVarInt(
-          MakeTagLengthDelimited(protos::Trace::kPacketFieldNumber), pos);
-      pos = WriteVarInt(static_cast<uint32_t>(slice.size), pos);
-      fwrite(reinterpret_cast<const char*>(preamble), pos - preamble, 1,
-             trace_out_stream_.get());
       fwrite(reinterpret_cast<const char*>(slice.start), slice.size, 1,
              trace_out_stream_.get());
     }
diff --git a/src/process_stats/procfs_utils.cc b/src/process_stats/procfs_utils.cc
index a0a9cbd..98b949e 100644
--- a/src/process_stats/procfs_utils.cc
+++ b/src/process_stats/procfs_utils.cc
@@ -67,7 +67,9 @@
 std::unique_ptr<ProcessInfo> ReadProcessInfo(int pid) {
   ProcessInfo* process = new ProcessInfo();
   process->pid = pid;
-  char cmdline_buf[256];
+  // It's not enough to just null terminate this since cmdline uses null as
+  // the argument seperator:
+  char cmdline_buf[256]{};
   ReadProcString(pid, "cmdline", cmdline_buf, sizeof(cmdline_buf));
   if (cmdline_buf[0] == 0) {
     // Nothing in cmdline_buf so read name from /comm instead.
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index 0d67278..c7f3d95 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -30,6 +30,7 @@
     "message.cc",
     "message_handle.cc",
     "proto_utils.cc",
+    "scattered_stream_null_delegate.cc",
     "scattered_stream_writer.cc",
   ]
 }
diff --git a/src/ftrace_reader/test/scattered_stream_null_delegate.cc b/src/protozero/scattered_stream_null_delegate.cc
similarity index 60%
rename from src/ftrace_reader/test/scattered_stream_null_delegate.cc
rename to src/protozero/scattered_stream_null_delegate.cc
index 562a3db..3587dba 100644
--- a/src/ftrace_reader/test/scattered_stream_null_delegate.cc
+++ b/src/protozero/scattered_stream_null_delegate.cc
@@ -14,22 +14,24 @@
  * limitations under the License.
  */
 
-#include "src/ftrace_reader/test/scattered_stream_null_delegate.h"
+#include "perfetto/protozero/scattered_stream_null_delegate.h"
 
-namespace perfetto {
+namespace protozero {
 
 // An implementation of ScatteredStreamWriter::Delegate which always returns
-// the same bit of memory (to better measure performance of users of
-// ScatteredStreamWriter without noisy allocations).
-
-ScatteredStreamNullDelegate::ScatteredStreamNullDelegate(size_t chunk_size)
+// the same piece of memory.
+// This is used when we need to no-op the writers (e.g. during teardown or in
+// case of resource exhaustion), avoiding that the clients have to deal with
+// nullptr checks.
+ScatteredStreamWriterNullDelegate::ScatteredStreamWriterNullDelegate(
+    size_t chunk_size)
     : chunk_size_(chunk_size),
       chunk_(std::unique_ptr<uint8_t[]>(new uint8_t[chunk_size_])){};
 
-ScatteredStreamNullDelegate::~ScatteredStreamNullDelegate() {}
+ScatteredStreamWriterNullDelegate::~ScatteredStreamWriterNullDelegate() {}
 
-protozero::ContiguousMemoryRange ScatteredStreamNullDelegate::GetNewBuffer() {
+ContiguousMemoryRange ScatteredStreamWriterNullDelegate::GetNewBuffer() {
   return {chunk_.get(), chunk_.get() + chunk_size_};
 }
 
-}  // namespace perfetto
+}  // namespace protozero
diff --git a/src/traced/probes/filesystem/BUILD.gn b/src/traced/probes/filesystem/BUILD.gn
index ce481d6..6beb973 100644
--- a/src/traced/probes/filesystem/BUILD.gn
+++ b/src/traced/probes/filesystem/BUILD.gn
@@ -22,6 +22,8 @@
     "../../../base",
   ]
   sources = [
+    "file_scanner.cc",
+    "file_scanner.h",
     "fs_mount.cc",
     "fs_mount.h",
     "inode_file_data_source.cc",
diff --git a/src/traced/probes/filesystem/file_scanner.cc b/src/traced/probes/filesystem/file_scanner.cc
new file mode 100644
index 0000000..8369e50
--- /dev/null
+++ b/src/traced/probes/filesystem/file_scanner.cc
@@ -0,0 +1,153 @@
+/*
+ * 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 "src/traced/probes/filesystem/file_scanner.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "src/traced/probes/filesystem/inode_file_data_source.h"
+
+namespace perfetto {
+namespace {
+
+std::string JoinPaths(const std::string& one, const std::string& other) {
+  std::string result;
+  result.reserve(one.size() + other.size() + 1);
+  result += one;
+  if (!result.empty() && result.back() != '/')
+    result += '/';
+  result += other;
+  return result;
+}
+
+}  // namespace
+
+FileScanner::FileScanner(std::vector<std::string> root_directories,
+                         Delegate* delegate,
+                         uint64_t scan_interval_ms,
+                         uint64_t scan_steps)
+    : delegate_(delegate),
+      scan_interval_ms_(scan_interval_ms),
+      scan_steps_(scan_steps),
+      queue_(std::move(root_directories)),
+      weak_factory_(this) {}
+
+FileScanner::FileScanner(std::vector<std::string> root_directories,
+                         Delegate* delegate)
+    : FileScanner(std::move(root_directories),
+                  delegate,
+                  0 /* scan_interval_ms */,
+                  0 /* scan_steps */) {}
+
+void FileScanner::Scan() {
+  while (!Done())
+    Step();
+}
+void FileScanner::Scan(base::TaskRunner* task_runner) {
+  PERFETTO_DCHECK(scan_interval_ms_ && scan_steps_);
+  Steps(scan_steps_);
+  if (Done())
+    return delegate_->OnInodeScanDone();
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner->PostDelayedTask(
+      [weak_this, task_runner] {
+        if (!weak_this)
+          return;
+        weak_this->Scan(task_runner);
+      },
+      scan_interval_ms_);
+}
+
+void FileScanner::NextDirectory() {
+  std::string directory = std::move(queue_.back());
+  queue_.pop_back();
+  current_dir_handle_.reset(opendir(directory.c_str()));
+  if (!current_dir_handle_) {
+    PERFETTO_DPLOG("opendir %s", directory.c_str());
+    current_directory_.clear();
+    return;
+  }
+  current_directory_ = std::move(directory);
+
+  struct stat buf;
+  if (fstat(dirfd(current_dir_handle_.get()), &buf) != 0) {
+    PERFETTO_DPLOG("fstat %s", current_directory_.c_str());
+    current_dir_handle_.reset();
+    current_directory_.clear();
+    return;
+  }
+
+  if (S_ISLNK(buf.st_mode)) {
+    current_dir_handle_.reset();
+    current_directory_.clear();
+    return;
+  }
+  current_block_device_id_ = buf.st_dev;
+}
+
+void FileScanner::Step() {
+  if (!current_dir_handle_) {
+    if (queue_.empty())
+      return;
+    NextDirectory();
+  }
+
+  if (!current_dir_handle_)
+    return;
+
+  struct dirent* entry = readdir(current_dir_handle_.get());
+  if (entry == nullptr) {
+    current_dir_handle_.reset();
+    return;
+  }
+
+  std::string filename = entry->d_name;
+  if (filename == "." || filename == "..")
+    return;
+
+  std::string filepath = JoinPaths(current_directory_, filename);
+
+  protos::pbzero::InodeFileMap_Entry_Type type =
+      protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
+  // Readdir and stat not guaranteed to have directory info for all systems
+  if (entry->d_type == DT_DIR) {
+    // Continue iterating through files if current entry is a directory
+    queue_.emplace_back(filepath);
+    type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
+  } else if (entry->d_type == DT_REG) {
+    type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
+  }
+
+  if (!delegate_->OnInodeFound(current_block_device_id_, entry->d_ino, filepath,
+                               type)) {
+    queue_.clear();
+    current_dir_handle_.reset();
+  }
+}
+
+void FileScanner::Steps(uint64_t n) {
+  for (uint64_t i = 0; i < n && !Done(); ++i)
+    Step();
+}
+
+bool FileScanner::Done() {
+  return !current_dir_handle_ && queue_.empty();
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/filesystem/file_scanner.h b/src/traced/probes/filesystem/file_scanner.h
new file mode 100644
index 0000000..bf193e4
--- /dev/null
+++ b/src/traced/probes/filesystem/file_scanner.h
@@ -0,0 +1,75 @@
+/*
+ * 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_TRACED_PROBES_FILESYSTEM_FILE_SCANNER_H_
+#define SRC_TRACED_PROBES_FILESYSTEM_FILE_SCANNER_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/weak_ptr.h"
+#include "perfetto/traced/data_source_types.h"
+
+namespace perfetto {
+
+class FileScanner {
+ public:
+  class Delegate {
+   public:
+    virtual bool OnInodeFound(BlockDeviceID,
+                              Inode,
+                              const std::string&,
+                              protos::pbzero::InodeFileMap_Entry_Type) = 0;
+    virtual void OnInodeScanDone() = 0;
+    virtual ~Delegate() {}
+  };
+
+  FileScanner(std::vector<std::string> root_directories,
+              Delegate* delegate,
+              uint64_t scan_interval_ms,
+              uint64_t scan_steps);
+
+  // Ctor when only the blocking version of Scan is used.
+  FileScanner(std::vector<std::string> root_directories, Delegate* delegate);
+
+  FileScanner(const FileScanner&) = delete;
+  FileScanner& operator=(const FileScanner&) = delete;
+
+  void Scan(base::TaskRunner* task_runner);
+  void Scan();
+
+ private:
+  void NextDirectory();
+  void Step();
+  void Steps(uint64_t n);
+  bool Done();
+
+  Delegate* delegate_;
+  const uint64_t scan_interval_ms_;
+  const uint64_t scan_steps_;
+
+  std::vector<std::string> queue_;
+  base::ScopedDir current_dir_handle_;
+  std::string current_directory_;
+  BlockDeviceID current_block_device_id_;
+  base::WeakPtrFactory<FileScanner> weak_factory_;  // Keep last.
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FILESYSTEM_FILE_SCANNER_H_
diff --git a/src/traced/probes/filesystem/inode_file_data_source.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
index 3186b3f..fd70ff7 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.cc
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -29,77 +29,73 @@
 #include "perfetto/tracing/core/trace_writer.h"
 
 #include "perfetto/trace/trace_packet.pbzero.h"
+#include "src/traced/probes/filesystem/file_scanner.h"
 
 namespace perfetto {
+namespace {
+constexpr uint64_t kScanIntervalMs = 10000;  // 10s
+constexpr uint64_t kScanDelayMs = 10000;     // 10s
+constexpr uint64_t kScanBatchSize = 15000;
 
-void ScanFilesDFS(
-    const std::string& root_directory,
-    const std::function<bool(BlockDeviceID block_device_id,
-                             Inode inode_number,
-                             const std::string& path,
-                             protos::pbzero::InodeFileMap_Entry_Type type)>&
-        fn) {
-  std::vector<std::string> queue{root_directory};
-  while (!queue.empty()) {
-    struct dirent* entry;
-    std::string directory = queue.back();
-    queue.pop_back();
-    base::ScopedDir dir(opendir(directory.c_str()));
-    directory += "/";
-    if (!dir)
-      continue;
-
-    struct stat buf;
-    if (lstat(directory.c_str(), &buf) != 0) {
-      PERFETTO_DPLOG("lstat %s", directory.c_str());
-      continue;
-    }
-    if (S_ISLNK(buf.st_mode))
-      continue;
-
-    BlockDeviceID block_device_id = buf.st_dev;
-
-    while ((entry = readdir(dir.get())) != nullptr) {
-      std::string filename = entry->d_name;
-      if (filename == "." || filename == "..")
-        continue;
-      std::string filepath = directory + filename;
-
-      Inode inode_number = entry->d_ino;
-
-      protos::pbzero::InodeFileMap_Entry_Type type =
-          protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
-      // Readdir and stat not guaranteed to have directory info for all systems
-      if (entry->d_type == DT_DIR) {
-        // Continue iterating through files if current entry is a directory
-        queue.push_back(filepath);
-        type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
-      } else if (entry->d_type == DT_REG) {
-        type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
-      }
-
-      if (!fn(block_device_id, inode_number, filepath, type))
-        return;
-    }
-    if (errno != 0)
-      PERFETTO_DPLOG("readdir %s", directory.c_str());
-  }
+uint64_t OrDefault(uint64_t value, uint64_t def) {
+  if (value != 0)
+    return value;
+  return def;
 }
 
+std::string DbgFmt(const std::vector<std::string>& values) {
+  if (values.empty())
+    return "";
+
+  std::string result;
+  for (auto it = values.cbegin(); it != values.cend() - 1; ++it)
+    result += *it + ",";
+
+  result += values.back();
+  return result;
+}
+
+std::map<std::string, std::vector<std::string>> BuildMountpointMapping(
+    const DataSourceConfig& source_config) {
+  std::map<std::string, std::vector<std::string>> m;
+  for (const auto& map_entry :
+       source_config.inode_file_config().mount_point_mapping())
+    m.emplace(map_entry.mountpoint(), map_entry.scan_roots());
+
+  return m;
+}
+
+class StaticMapDelegate : public FileScanner::Delegate {
+ public:
+  StaticMapDelegate(
+      std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map)
+      : map_(map) {}
+  ~StaticMapDelegate() {}
+
+ private:
+  bool OnInodeFound(BlockDeviceID block_device_id,
+                    Inode inode_number,
+                    const std::string& path,
+                    protos::pbzero::InodeFileMap_Entry_Type type) {
+    std::unordered_map<Inode, InodeMapValue>& inode_map =
+        (*map_)[block_device_id];
+    inode_map[inode_number].SetType(type);
+    inode_map[inode_number].AddPath(path);
+    return true;
+  }
+  void OnInodeScanDone() {}
+  std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>* map_;
+};
+
+}  // namespace
+
 void CreateStaticDeviceToInodeMap(
     const std::string& root_directory,
     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
         static_file_map) {
-  ScanFilesDFS(root_directory,
-               [static_file_map](BlockDeviceID block_device_id,
-                                 Inode inode_number, const std::string& path,
-                                 protos::pbzero::InodeFileMap_Entry_Type type) {
-                 std::unordered_map<Inode, InodeMapValue>& inode_map =
-                     (*static_file_map)[block_device_id];
-                 inode_map[inode_number].SetType(type);
-                 inode_map[inode_number].AddPath(path);
-                 return true;
-               });
+  StaticMapDelegate delegate(static_file_map);
+  FileScanner scanner({root_directory}, &delegate);
+  scanner.Scan();
 }
 
 void FillInodeEntry(InodeFileMap* destination,
@@ -113,63 +109,28 @@
 }
 
 InodeFileDataSource::InodeFileDataSource(
+    DataSourceConfig source_config,
+    base::TaskRunner* task_runner,
     TracingSessionID id,
     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
         static_file_map,
     LRUInodeCache* cache,
     std::unique_ptr<TraceWriter> writer)
-    : session_id_(id),
+    : source_config_(std::move(source_config)),
+      scan_mount_points_(
+          source_config_.inode_file_config().scan_mount_points().cbegin(),
+          source_config_.inode_file_config().scan_mount_points().cend()),
+      mount_point_mapping_(BuildMountpointMapping(source_config_)),
+      task_runner_(task_runner),
+      session_id_(id),
       static_file_map_(static_file_map),
       cache_(cache),
       writer_(std::move(writer)),
       weak_factory_(this) {}
 
-void InodeFileDataSource::AddInodesFromFilesystemScan(
-    const std::string& root_directory,
-    BlockDeviceID provided_block_device_id,
-    std::set<Inode>* inode_numbers,
-    LRUInodeCache* cache,
-    InodeFileMap* destination) {
-  if (inode_numbers->empty())
-    return;
-  ScanFilesDFS(
-      root_directory,
-      [provided_block_device_id, inode_numbers, cache, destination](
-          BlockDeviceID block_device_id, Inode inode_number,
-          const std::string& path,
-          protos::pbzero::InodeFileMap_Entry_Type type) {
-        if (provided_block_device_id != block_device_id)
-          return true;
-        if (inode_numbers->find(inode_number) == inode_numbers->end())
-          return true;
-        std::pair<BlockDeviceID, Inode> key{block_device_id, inode_number};
-        auto cur_val = cache->Get(key);
-        if (cur_val != nullptr) {
-          cur_val->AddPath(path);
-          FillInodeEntry(destination, inode_number, *cur_val);
-        } else {
-          InodeMapValue new_val(InodeMapValue(type, {path}));
-          cache->Insert(key, new_val);
-          FillInodeEntry(destination, inode_number, new_val);
-        }
-        inode_numbers->erase(inode_number);
-        if (inode_numbers->empty())
-          return false;
-        return true;
-      });
-
-  // Could not be found, just add the inode number
-  if (inode_numbers->size() != 0)
-    PERFETTO_DLOG("%zu inodes not found", inode_numbers->size());
-  for (const auto& unresolved_inode : *inode_numbers) {
-    auto* entry = destination->add_entries();
-    entry->set_inode_number(unresolved_inode);
-  }
-}
-
-void InodeFileDataSource::AddInodesFromStaticMap(BlockDeviceID block_device_id,
-                                                 std::set<Inode>* inode_numbers,
-                                                 InodeFileMap* destination) {
+void InodeFileDataSource::AddInodesFromStaticMap(
+    BlockDeviceID block_device_id,
+    std::set<Inode>* inode_numbers) {
   // Check if block device id exists in static file map
   auto static_map_entry = static_file_map_->find(block_device_id);
   if (static_map_entry == static_file_map_->end())
@@ -186,15 +147,16 @@
     }
     system_found_count++;
     it = inode_numbers->erase(it);
-    FillInodeEntry(destination, inode_number, inode_it->second);
+    FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
+                   inode_it->second);
   }
   PERFETTO_DLOG("%" PRIu64 " inodes found in static file map",
                 system_found_count);
 }
 
-void InodeFileDataSource::AddInodesFromLRUCache(BlockDeviceID block_device_id,
-                                                std::set<Inode>* inode_numbers,
-                                                InodeFileMap* destination) {
+void InodeFileDataSource::AddInodesFromLRUCache(
+    BlockDeviceID block_device_id,
+    std::set<Inode>* inode_numbers) {
   uint64_t cache_found_count = 0;
   for (auto it = inode_numbers->begin(); it != inode_numbers->end();) {
     Inode inode_number = *it;
@@ -205,7 +167,8 @@
     }
     cache_found_count++;
     it = inode_numbers->erase(it);
-    FillInodeEntry(destination, inode_number, *value);
+    FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
+                   *value);
   }
   if (cache_found_count > 0)
     PERFETTO_DLOG("%" PRIu64 " inodes found in cache", cache_found_count);
@@ -223,37 +186,187 @@
     BlockDeviceID block_device_id = inodes_pair.second;
     inode_file_maps[block_device_id].emplace(inode_number);
   }
-  PERFETTO_DLOG("Saw %zu block devices.", inode_file_maps.size());
+  if (inode_file_maps.size() > 1)
+    PERFETTO_DLOG("Saw %zu block devices.", inode_file_maps.size());
 
   // Write a TracePacket with an InodeFileMap proto for each block device id
-  for (const auto& inode_file_map_data : inode_file_maps) {
+  for (auto& inode_file_map_data : inode_file_maps) {
     BlockDeviceID block_device_id = inode_file_map_data.first;
-    std::set<Inode> inode_numbers = inode_file_map_data.second;
+    std::set<Inode>& inode_numbers = inode_file_map_data.second;
     PERFETTO_DLOG("Saw %zu unique inode numbers.", inode_numbers.size());
 
-    // New TracePacket for each InodeFileMap
-    auto trace_packet = writer_->NewTracePacket();
-    auto inode_file_map = trace_packet->set_inode_file_map();
+    // Add entries to InodeFileMap as inodes are found and resolved to their
+    // paths/type
+    AddInodesFromStaticMap(block_device_id, &inode_numbers);
+    AddInodesFromLRUCache(block_device_id, &inode_numbers);
+
+    if (source_config_.inode_file_config().do_not_scan())
+      inode_numbers.clear();
+
+    // If we defined mount points we want to scan in the config,
+    // skip inodes on other mount points.
+    if (!scan_mount_points_.empty()) {
+      bool process = true;
+      auto range = mount_points_.equal_range(block_device_id);
+      for (auto it = range.first; it != range.second; ++it) {
+        if (scan_mount_points_.count(it->second) == 0) {
+          process = false;
+          break;
+        }
+      }
+      if (!process)
+        continue;
+    }
+
+    if (!inode_numbers.empty()) {
+      // Try to piggy back the current scan.
+      auto it = missing_inodes_.find(block_device_id);
+      if (it != missing_inodes_.end()) {
+        it->second.insert(inode_numbers.cbegin(), inode_numbers.cend());
+      }
+      next_missing_inodes_[block_device_id].insert(inode_numbers.cbegin(),
+                                                   inode_numbers.cend());
+      if (!scan_running_) {
+        scan_running_ = true;
+        auto weak_this = GetWeakPtr();
+        task_runner_->PostDelayedTask(
+            [weak_this] {
+              if (!weak_this) {
+                PERFETTO_DLOG("Giving up filesystem scan.");
+                return;
+              }
+              weak_this.get()->FindMissingInodes();
+            },
+            GetScanDelayMs());
+      }
+    }
+  }
+}
+
+InodeFileMap* InodeFileDataSource::AddToCurrentTracePacket(
+    BlockDeviceID block_device_id) {
+  if (!has_current_trace_packet_ ||
+      current_block_device_id_ != block_device_id) {
+    if (has_current_trace_packet_)
+      current_trace_packet_->Finalize();
+    current_trace_packet_ = writer_->NewTracePacket();
+    current_file_map_ = current_trace_packet_->set_inode_file_map();
+    has_current_trace_packet_ = true;
 
     // Add block device id to InodeFileMap
-    inode_file_map->set_block_device_id(block_device_id);
-
+    current_file_map_->set_block_device_id(block_device_id);
     // Add mount points to InodeFileMap
     auto range = mount_points_.equal_range(block_device_id);
     for (std::multimap<BlockDeviceID, std::string>::iterator it = range.first;
          it != range.second; ++it)
-      inode_file_map->add_mount_points(it->second.c_str());
-
-    // Add entries to InodeFileMap as inodes are found and resolved to their
-    // paths/type
-    AddInodesFromStaticMap(block_device_id, &inode_numbers, inode_file_map);
-    AddInodesFromLRUCache(block_device_id, &inode_numbers, inode_file_map);
-    // TODO(azappone): Make root directory a mount point
-    std::string root_directory = "/data";
-    AddInodesFromFilesystemScan(root_directory, block_device_id, &inode_numbers,
-                                cache_, inode_file_map);
-    trace_packet->Finalize();
+      current_file_map_->add_mount_points(it->second.c_str());
   }
+  return current_file_map_;
+}
+
+bool InodeFileDataSource::OnInodeFound(
+    BlockDeviceID block_device_id,
+    Inode inode_number,
+    const std::string& path,
+    protos::pbzero::InodeFileMap_Entry_Type type) {
+  auto it = missing_inodes_.find(block_device_id);
+  if (it == missing_inodes_.end())
+    return true;
+
+  size_t n = it->second.erase(inode_number);
+  if (n == 0)
+    return true;
+
+  if (it->second.empty())
+    missing_inodes_.erase(it);
+
+  std::pair<BlockDeviceID, Inode> key{block_device_id, inode_number};
+  auto cur_val = cache_->Get(key);
+  if (cur_val) {
+    cur_val->AddPath(path);
+    FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
+                   *cur_val);
+  } else {
+    InodeMapValue new_val(InodeMapValue(type, {path}));
+    cache_->Insert(key, new_val);
+    FillInodeEntry(AddToCurrentTracePacket(block_device_id), inode_number,
+                   new_val);
+  }
+  PERFETTO_DLOG("Filled %s", path.c_str());
+  return !missing_inodes_.empty();
+}
+
+void InodeFileDataSource::OnInodeScanDone() {
+  // Finalize the accumulated trace packets.
+  current_block_device_id_ = 0;
+  current_file_map_ = nullptr;
+  if (has_current_trace_packet_)
+    current_trace_packet_->Finalize();
+  has_current_trace_packet_ = false;
+  file_scanner_.reset();
+  if (next_missing_inodes_.empty()) {
+    scan_running_ = false;
+  } else {
+    auto weak_this = GetWeakPtr();
+    PERFETTO_DLOG("Starting another filesystem scan.");
+    task_runner_->PostDelayedTask(
+        [weak_this] {
+          if (!weak_this) {
+            PERFETTO_DLOG("Giving up filesystem scan.");
+            return;
+          }
+          weak_this->FindMissingInodes();
+        },
+        GetScanDelayMs());
+  }
+}
+
+void InodeFileDataSource::AddRootsForBlockDevice(
+    BlockDeviceID block_device_id,
+    std::vector<std::string>* roots) {
+  auto range = mount_points_.equal_range(block_device_id);
+  for (auto it = range.first; it != range.second; ++it) {
+    PERFETTO_DLOG("Trying to replace %s", it->second.c_str());
+    auto replace_it = mount_point_mapping_.find(it->second);
+    if (replace_it != mount_point_mapping_.end()) {
+      roots->insert(roots->end(), replace_it->second.cbegin(),
+                    replace_it->second.cend());
+      return;
+    }
+  }
+
+  for (auto it = range.first; it != range.second; ++it)
+    roots->emplace_back(it->second);
+}
+
+void InodeFileDataSource::FindMissingInodes() {
+  missing_inodes_ = std::move(next_missing_inodes_);
+  std::vector<std::string> roots;
+  for (auto& p : missing_inodes_)
+    AddRootsForBlockDevice(p.first, &roots);
+
+  PERFETTO_DCHECK(file_scanner_.get() == nullptr);
+  auto weak_this = GetWeakPtr();
+  PERFETTO_DLOG("Starting scan of %s", DbgFmt(roots).c_str());
+  file_scanner_ = std::unique_ptr<FileScanner>(new FileScanner(
+      std::move(roots), this, GetScanIntervalMs(), GetScanBatchSize()));
+
+  file_scanner_->Scan(task_runner_);
+}
+
+uint64_t InodeFileDataSource::GetScanIntervalMs() {
+  return OrDefault(source_config_.inode_file_config().scan_interval_ms(),
+                   kScanIntervalMs);
+}
+
+uint64_t InodeFileDataSource::GetScanDelayMs() {
+  return OrDefault(source_config_.inode_file_config().scan_delay_ms(),
+                   kScanDelayMs);
+}
+
+uint64_t InodeFileDataSource::GetScanBatchSize() {
+  return OrDefault(source_config_.inode_file_config().scan_batch_size(),
+                   kScanBatchSize);
 }
 
 base::WeakPtr<InodeFileDataSource> InodeFileDataSource::GetWeakPtr() const {
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index af881d4..1f4197f 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -25,10 +25,13 @@
 #include <string>
 #include <unordered_map>
 
+#include "perfetto/base/task_runner.h"
 #include "perfetto/base/weak_ptr.h"
 #include "perfetto/traced/data_source_types.h"
 #include "perfetto/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/trace_writer.h"
+#include "src/traced/probes/filesystem/file_scanner.h"
 #include "src/traced/probes/filesystem/fs_mount.h"
 #include "src/traced/probes/filesystem/lru_inode_cache.h"
 
@@ -56,9 +59,11 @@
                     Inode inode_number,
                     const InodeMapValue& inode_map_value);
 
-class InodeFileDataSource {
+class InodeFileDataSource : public FileScanner::Delegate {
  public:
   InodeFileDataSource(
+      DataSourceConfig,
+      base::TaskRunner*,
       TracingSessionID,
       std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
           static_file_map,
@@ -71,32 +76,50 @@
   // Called when Inodes are seen in the FtraceEventBundle
   void OnInodes(const std::vector<std::pair<Inode, BlockDeviceID>>& inodes);
 
-  // Filesystem scan starting from root directory to search for provided inode
-  // numbers. Adds all inode numbers to the InodeFileMap proto. Fills in cache
-  // for all inode numbers that get resolved from the scan.
-  void AddInodesFromFilesystemScan(const std::string& root_directory,
-                                   BlockDeviceID block_device_id,
-                                   std::set<Inode>* inode_numbers,
-                                   LRUInodeCache* cache,
-                                   InodeFileMap* destination);
-
   // Search in /system partition and add inodes to InodeFileMap proto if found
   void AddInodesFromStaticMap(BlockDeviceID block_device_id,
-                              std::set<Inode>* inode_numbers,
-                              InodeFileMap* destination);
+                              std::set<Inode>* inode_numbers);
 
   // Search in LRUInodeCache and add inodes to InodeFileMap if found
   void AddInodesFromLRUCache(BlockDeviceID block_device_id,
-                             std::set<Inode>* inode_numbers,
-                             InodeFileMap* destination);
+                             std::set<Inode>* inode_numbers);
+
+  virtual ~InodeFileDataSource() {}
 
  private:
+  InodeFileMap* AddToCurrentTracePacket(BlockDeviceID block_device_id);
+  void FindMissingInodes();
+  bool OnInodeFound(BlockDeviceID block_device_id,
+                    Inode inode_number,
+                    const std::string& path,
+                    protos::pbzero::InodeFileMap_Entry_Type type);
+  void OnInodeScanDone();
+  void AddRootsForBlockDevice(BlockDeviceID block_device_id,
+                              std::vector<std::string>* roots);
+
+  uint64_t GetScanIntervalMs();
+  uint64_t GetScanDelayMs();
+  uint64_t GetScanBatchSize();
+
+  const DataSourceConfig source_config_;
+  std::set<std::string> scan_mount_points_;
+  std::map<std::string, std::vector<std::string>> mount_point_mapping_;
+
+  base::TaskRunner* task_runner_;
   const TracingSessionID session_id_;
   std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
       static_file_map_;
   LRUInodeCache* cache_;
   std::multimap<BlockDeviceID, std::string> mount_points_;
   std::unique_ptr<TraceWriter> writer_;
+  std::map<BlockDeviceID, std::set<Inode>> missing_inodes_;
+  std::map<BlockDeviceID, std::set<Inode>> next_missing_inodes_;
+  BlockDeviceID current_block_device_id_;
+  TraceWriter::TracePacketHandle current_trace_packet_;
+  InodeFileMap* current_file_map_;
+  bool has_current_trace_packet_ = false;
+  bool scan_running_ = false;
+  std::unique_ptr<FileScanner> file_scanner_;
   base::WeakPtrFactory<InodeFileDataSource> weak_factory_;  // Keep last.
 };
 
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 5d29fd9..3c3a532 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -18,6 +18,8 @@
 
 #include <stdio.h>
 #include <sys/stat.h>
+
+#include <algorithm>
 #include <queue>
 #include <string>
 
@@ -40,9 +42,9 @@
 
 uint64_t kInitialConnectionBackoffMs = 100;
 uint64_t kMaxConnectionBackoffMs = 30 * 1000;
-constexpr char kFtraceSourceName[] = "com.google.perfetto.ftrace";
-constexpr char kProcessStatsSourceName[] = "com.google.perfetto.process_stats";
-constexpr char kInodeMapSourceName[] = "com.google.perfetto.inode_file_map";
+constexpr char kFtraceSourceName[] = "linux.ftrace";
+constexpr char kProcessStatsSourceName[] = "linux.process_stats";
+constexpr char kInodeMapSourceName[] = "linux.inode_file_map";
 
 }  // namespace.
 
@@ -65,17 +67,15 @@
 
   DataSourceDescriptor ftrace_descriptor;
   ftrace_descriptor.set_name(kFtraceSourceName);
-  endpoint_->RegisterDataSource(ftrace_descriptor, [](DataSourceInstanceID) {});
+  endpoint_->RegisterDataSource(ftrace_descriptor);
 
   DataSourceDescriptor process_stats_descriptor;
   process_stats_descriptor.set_name(kProcessStatsSourceName);
-  endpoint_->RegisterDataSource(process_stats_descriptor,
-                                [](DataSourceInstanceID) {});
+  endpoint_->RegisterDataSource(process_stats_descriptor);
 
   DataSourceDescriptor inode_map_descriptor;
   inode_map_descriptor.set_name(kInodeMapSourceName);
-  endpoint_->RegisterDataSource(inode_map_descriptor,
-                                [](DataSourceInstanceID) {});
+  endpoint_->RegisterDataSource(inode_map_descriptor);
 }
 
 void ProbesProducer::OnDisconnect() {
@@ -182,16 +182,17 @@
 void ProbesProducer::CreateInodeFileDataSourceInstance(
     TracingSessionID session_id,
     DataSourceInstanceID id,
-    const DataSourceConfig& source_config) {
+    DataSourceConfig source_config) {
   PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
                id, source_config.target_buffer());
   auto trace_writer = endpoint_->CreateTraceWriter(
       static_cast<BufferID>(source_config.target_buffer()));
   if (system_inodes_.empty())
-    CreateStaticDeviceToInodeMap("/system/", &system_inodes_);
+    CreateStaticDeviceToInodeMap("/system", &system_inodes_);
   auto file_map_source =
       std::unique_ptr<InodeFileDataSource>(new InodeFileDataSource(
-          session_id, &system_inodes_, &cache_, std::move(trace_writer)));
+          std::move(source_config), task_runner_, session_id, &system_inodes_,
+          &cache_, std::move(trace_writer)));
   file_map_sources_.emplace(id, std::move(file_map_source));
   AddWatchdogsTimer(id, source_config);
 }
@@ -207,6 +208,13 @@
       new ProcessStatsDataSource(session_id, std::move(trace_writer)));
   auto it_and_inserted = process_stats_sources_.emplace(id, std::move(source));
   PERFETTO_DCHECK(it_and_inserted.second);
+  if (std::find(config.process_stats_config().quirks().begin(),
+                config.process_stats_config().quirks().end(),
+                ProcessStatsConfig::DISABLE_INITIAL_DUMP) !=
+      config.process_stats_config().quirks().end()) {
+    PERFETTO_DLOG("Initial process tree dump is disabled.");
+    return;
+  }
   it_and_inserted.first->second->WriteAllProcesses();
 }
 
@@ -241,7 +249,7 @@
   PERFETTO_DCHECK(state_ == kNotConnected);
   state_ = kConnecting;
   endpoint_ = ProducerIPCClient::Connect(
-      socket_name_, this, "com.google.perfetto.traced_probes", task_runner_);
+      socket_name_, this, "perfetto.traced_probes", task_runner_);
 }
 
 void ProbesProducer::IncreaseConnectionBackoff() {
@@ -276,6 +284,15 @@
     const FtraceMetadata& metadata) {
   trace_packet_->Finalize();
 
+  if (ps_source_ && !metadata.pids.empty()) {
+    const auto& pids = metadata.pids;
+    auto weak_ps_source = ps_source_;
+    task_runner_->PostTask([weak_ps_source, pids] {
+      if (weak_ps_source)
+        weak_ps_source->OnPids(pids);
+    });
+  }
+
   if (file_source_ && !metadata.inode_and_device.empty()) {
     auto inodes = metadata.inode_and_device;
     auto weak_file_source = file_source_;
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index bd550c7..eb45935 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -60,7 +60,7 @@
                                             const DataSourceConfig& config);
   void CreateInodeFileDataSourceInstance(TracingSessionID session_id,
                                          DataSourceInstanceID id,
-                                         const DataSourceConfig& config);
+                                         DataSourceConfig config);
 
   void OnMetadata(const FtraceMetadata& metadata);
 
diff --git a/src/traced/probes/process_stats_data_source.cc b/src/traced/probes/process_stats_data_source.cc
index 27f05ba..a1bffd8 100644
--- a/src/traced/probes/process_stats_data_source.cc
+++ b/src/traced/probes/process_stats_data_source.cc
@@ -20,7 +20,6 @@
 
 #include "perfetto/trace/ps/process_tree.pbzero.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
-#include "perfetto/tracing/core/trace_packet.h"
 #include "src/process_stats/file_utils.h"
 #include "src/process_stats/procfs_utils.h"
 
@@ -39,36 +38,51 @@
 }
 
 void ProcessStatsDataSource::WriteAllProcesses() {
-  procfs_utils::ProcessMap processes;
   auto trace_packet = writer_->NewTracePacket();
-  protos::pbzero::ProcessTree* process_tree = trace_packet->set_process_tree();
+  auto* trace_packet_ptr = &*trace_packet;
+  std::set<int32_t>* seen_pids = &seen_pids_;
 
-  file_utils::ForEachPidInProcPath(
-      "/proc", [&processes, process_tree](int pid) {
-        // ForEachPid will list all processes and threads. Here we want to
-        // iterate first only by processes (for which pid == thread group id)
-        if (!processes.count(pid)) {
-          if (procfs_utils::ReadTgid(pid) != pid)
-            return;
-          processes[pid] = procfs_utils::ReadProcessInfo(pid);
-        }
-        ProcessInfo* process = processes[pid].get();
-        procfs_utils::ReadProcessThreads(process);
-        auto* process_writer = process_tree->add_processes();
-        process_writer->set_pid(process->pid);
-        process_writer->set_ppid(process->ppid);
-        for (const auto& field : process->cmdline)
-          process_writer->add_cmdline(field.c_str());
-        for (auto& thread : process->threads) {
-          auto* thread_writer = process_writer->add_threads();
-          thread_writer->set_tid(thread.second.tid);
-          thread_writer->set_name(thread.second.name);
-        }
-      });
+  file_utils::ForEachPidInProcPath("/proc",
+                                   [trace_packet_ptr, seen_pids](int pid) {
+                                     // ForEachPid will list all processes and
+                                     // threads. Here we want to iterate first
+                                     // only by processes (for which pid ==
+                                     // thread group id)
+                                     if (procfs_utils::ReadTgid(pid) != pid)
+                                       return;
+
+                                     WriteProcess(pid, trace_packet_ptr);
+                                     seen_pids->insert(pid);
+                                   });
 }
 
 void ProcessStatsDataSource::OnPids(const std::vector<int32_t>& pids) {
-  PERFETTO_DLOG("Saw FtraceBundle with %zu pids.", pids.size());
+  auto trace_packet = writer_->NewTracePacket();
+  for (int32_t pid : pids) {
+    auto it_and_inserted = seen_pids_.emplace(pid);
+    if (it_and_inserted.second)
+      WriteProcess(pid, &*trace_packet);
+  }
+}
+
+// static
+void ProcessStatsDataSource::WriteProcess(
+    int32_t pid,
+    protos::pbzero::TracePacket* trace_packet) {
+  auto* process_tree = trace_packet->set_process_tree();
+
+  std::unique_ptr<ProcessInfo> process = procfs_utils::ReadProcessInfo(pid);
+  procfs_utils::ReadProcessThreads(process.get());
+  auto* process_writer = process_tree->add_processes();
+  process_writer->set_pid(process->pid);
+  process_writer->set_ppid(process->ppid);
+  for (const auto& field : process->cmdline)
+    process_writer->add_cmdline(field.c_str());
+  for (auto& thread : process->threads) {
+    auto* thread_writer = process_writer->add_threads();
+    thread_writer->set_tid(thread.second.tid);
+    thread_writer->set_name(thread.second.name);
+  }
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/process_stats_data_source.h b/src/traced/probes/process_stats_data_source.h
index 2b691ab..f134bff 100644
--- a/src/traced/probes/process_stats_data_source.h
+++ b/src/traced/probes/process_stats_data_source.h
@@ -18,6 +18,7 @@
 #define SRC_TRACED_PROBES_PROCESS_STATS_DATA_SOURCE_H_
 
 #include <memory>
+#include <set>
 #include <vector>
 
 #include "perfetto/base/weak_ptr.h"
@@ -37,11 +38,15 @@
   void OnPids(const std::vector<int32_t>& pids);
 
  private:
+  static void WriteProcess(int32_t pid, protos::pbzero::TracePacket*);
+
   ProcessStatsDataSource(const ProcessStatsDataSource&) = delete;
   ProcessStatsDataSource& operator=(const ProcessStatsDataSource&) = delete;
 
   const TracingSessionID session_id_;
   std::unique_ptr<TraceWriter> writer_;
+  // TODO(b/76663469): Optimization: use a bitmap.
+  std::set<int32_t> seen_pids_;
   base::WeakPtrFactory<ProcessStatsDataSource> weak_factory_;  // Keep last.
 };
 
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index eef490d..68eee89 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -37,9 +37,13 @@
     "core/ftrace_config.cc",
     "core/id_allocator.cc",
     "core/id_allocator.h",
+    "core/inode_file_config.cc",
+    "core/null_trace_writer.cc",
+    "core/null_trace_writer.h",
     "core/packet_stream_validator.cc",
     "core/packet_stream_validator.h",
     "core/patch_list.h",
+    "core/process_stats_config.cc",
     "core/service_impl.cc",
     "core/service_impl.h",
     "core/shared_memory_abi.cc",
@@ -122,6 +126,7 @@
   ]
   sources = [
     "core/id_allocator_unittest.cc",
+    "core/null_trace_writer_unittest.cc",
     "core/packet_stream_validator_unittest.cc",
     "core/patch_list_unittest.cc",
     "core/service_impl_unittest.cc",
diff --git a/src/tracing/core/data_source_config.cc b/src/tracing/core/data_source_config.cc
index 622eb4c..56192e8 100644
--- a/src/tracing/core/data_source_config.cc
+++ b/src/tracing/core/data_source_config.cc
@@ -30,6 +30,8 @@
 #include "perfetto/config/chrome/chrome_config.pb.h"
 #include "perfetto/config/data_source_config.pb.h"
 #include "perfetto/config/ftrace/ftrace_config.pb.h"
+#include "perfetto/config/inode_file/inode_file_config.pb.h"
+#include "perfetto/config/process_stats/process_stats_config.pb.h"
 #include "perfetto/config/test_config.pb.h"
 
 namespace perfetto {
@@ -60,6 +62,14 @@
 
   chrome_config_.FromProto(proto.chrome_config());
 
+  inode_file_config_.FromProto(proto.inode_file_config());
+
+  process_stats_config_.FromProto(proto.process_stats_config());
+
+  static_assert(sizeof(legacy_config_) == sizeof(proto.legacy_config()),
+                "size mismatch");
+  legacy_config_ = static_cast<decltype(legacy_config_)>(proto.legacy_config());
+
   for_testing_.FromProto(proto.for_testing());
   unknown_fields_ = proto.unknown_fields();
 }
@@ -86,6 +96,15 @@
 
   chrome_config_.ToProto(proto->mutable_chrome_config());
 
+  inode_file_config_.ToProto(proto->mutable_inode_file_config());
+
+  process_stats_config_.ToProto(proto->mutable_process_stats_config());
+
+  static_assert(sizeof(legacy_config_) == sizeof(proto->legacy_config()),
+                "size mismatch");
+  proto->set_legacy_config(
+      static_cast<decltype(proto->legacy_config())>(legacy_config_));
+
   for_testing_.ToProto(proto->mutable_for_testing());
   *(proto->mutable_unknown_fields()) = unknown_fields_;
 }
diff --git a/src/tracing/core/ftrace_config.cc b/src/tracing/core/ftrace_config.cc
index 34d8ef4..cf0891f 100644
--- a/src/tracing/core/ftrace_config.cc
+++ b/src/tracing/core/ftrace_config.cc
@@ -84,19 +84,21 @@
   proto->Clear();
 
   for (const auto& it : ftrace_events_) {
-    proto->add_ftrace_events(it);
+    proto->add_ftrace_events(
+        static_cast<decltype(proto->ftrace_events(0))>(it));
     static_assert(sizeof(it) == sizeof(proto->ftrace_events(0)),
                   "size mismatch");
   }
 
   for (const auto& it : atrace_categories_) {
-    proto->add_atrace_categories(it);
+    proto->add_atrace_categories(
+        static_cast<decltype(proto->atrace_categories(0))>(it));
     static_assert(sizeof(it) == sizeof(proto->atrace_categories(0)),
                   "size mismatch");
   }
 
   for (const auto& it : atrace_apps_) {
-    proto->add_atrace_apps(it);
+    proto->add_atrace_apps(static_cast<decltype(proto->atrace_apps(0))>(it));
     static_assert(sizeof(it) == sizeof(proto->atrace_apps(0)), "size mismatch");
   }
 
diff --git a/src/tracing/core/inode_file_config.cc b/src/tracing/core/inode_file_config.cc
new file mode 100644
index 0000000..bd831bc
--- /dev/null
+++ b/src/tracing/core/inode_file_config.cc
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/inode_file/inode_file_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#include "perfetto/tracing/core/inode_file_config.h"
+
+#include "perfetto/config/inode_file/inode_file_config.pb.h"
+
+namespace perfetto {
+
+InodeFileConfig::InodeFileConfig() = default;
+InodeFileConfig::~InodeFileConfig() = default;
+InodeFileConfig::InodeFileConfig(const InodeFileConfig&) = default;
+InodeFileConfig& InodeFileConfig::operator=(const InodeFileConfig&) = default;
+InodeFileConfig::InodeFileConfig(InodeFileConfig&&) noexcept = default;
+InodeFileConfig& InodeFileConfig::operator=(InodeFileConfig&&) = default;
+
+void InodeFileConfig::FromProto(
+    const perfetto::protos::InodeFileConfig& proto) {
+  static_assert(sizeof(scan_interval_ms_) == sizeof(proto.scan_interval_ms()),
+                "size mismatch");
+  scan_interval_ms_ =
+      static_cast<decltype(scan_interval_ms_)>(proto.scan_interval_ms());
+
+  static_assert(sizeof(scan_delay_ms_) == sizeof(proto.scan_delay_ms()),
+                "size mismatch");
+  scan_delay_ms_ = static_cast<decltype(scan_delay_ms_)>(proto.scan_delay_ms());
+
+  static_assert(sizeof(scan_batch_size_) == sizeof(proto.scan_batch_size()),
+                "size mismatch");
+  scan_batch_size_ =
+      static_cast<decltype(scan_batch_size_)>(proto.scan_batch_size());
+
+  static_assert(sizeof(do_not_scan_) == sizeof(proto.do_not_scan()),
+                "size mismatch");
+  do_not_scan_ = static_cast<decltype(do_not_scan_)>(proto.do_not_scan());
+
+  scan_mount_points_.clear();
+  for (const auto& field : proto.scan_mount_points()) {
+    scan_mount_points_.emplace_back();
+    static_assert(
+        sizeof(scan_mount_points_.back()) == sizeof(proto.scan_mount_points(0)),
+        "size mismatch");
+    scan_mount_points_.back() =
+        static_cast<decltype(scan_mount_points_)::value_type>(field);
+  }
+
+  mount_point_mapping_.clear();
+  for (const auto& field : proto.mount_point_mapping()) {
+    mount_point_mapping_.emplace_back();
+    mount_point_mapping_.back().FromProto(field);
+  }
+  unknown_fields_ = proto.unknown_fields();
+}
+
+void InodeFileConfig::ToProto(perfetto::protos::InodeFileConfig* proto) const {
+  proto->Clear();
+
+  static_assert(sizeof(scan_interval_ms_) == sizeof(proto->scan_interval_ms()),
+                "size mismatch");
+  proto->set_scan_interval_ms(
+      static_cast<decltype(proto->scan_interval_ms())>(scan_interval_ms_));
+
+  static_assert(sizeof(scan_delay_ms_) == sizeof(proto->scan_delay_ms()),
+                "size mismatch");
+  proto->set_scan_delay_ms(
+      static_cast<decltype(proto->scan_delay_ms())>(scan_delay_ms_));
+
+  static_assert(sizeof(scan_batch_size_) == sizeof(proto->scan_batch_size()),
+                "size mismatch");
+  proto->set_scan_batch_size(
+      static_cast<decltype(proto->scan_batch_size())>(scan_batch_size_));
+
+  static_assert(sizeof(do_not_scan_) == sizeof(proto->do_not_scan()),
+                "size mismatch");
+  proto->set_do_not_scan(
+      static_cast<decltype(proto->do_not_scan())>(do_not_scan_));
+
+  for (const auto& it : scan_mount_points_) {
+    proto->add_scan_mount_points(
+        static_cast<decltype(proto->scan_mount_points(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->scan_mount_points(0)),
+                  "size mismatch");
+  }
+
+  for (const auto& it : mount_point_mapping_) {
+    auto* entry = proto->add_mount_point_mapping();
+    it.ToProto(entry);
+  }
+  *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+InodeFileConfig::MountPointMappingEntry::MountPointMappingEntry() = default;
+InodeFileConfig::MountPointMappingEntry::~MountPointMappingEntry() = default;
+InodeFileConfig::MountPointMappingEntry::MountPointMappingEntry(
+    const InodeFileConfig::MountPointMappingEntry&) = default;
+InodeFileConfig::MountPointMappingEntry&
+InodeFileConfig::MountPointMappingEntry::operator=(
+    const InodeFileConfig::MountPointMappingEntry&) = default;
+InodeFileConfig::MountPointMappingEntry::MountPointMappingEntry(
+    InodeFileConfig::MountPointMappingEntry&&) noexcept = default;
+InodeFileConfig::MountPointMappingEntry&
+InodeFileConfig::MountPointMappingEntry::operator=(
+    InodeFileConfig::MountPointMappingEntry&&) = default;
+
+void InodeFileConfig::MountPointMappingEntry::FromProto(
+    const perfetto::protos::InodeFileConfig_MountPointMappingEntry& proto) {
+  static_assert(sizeof(mountpoint_) == sizeof(proto.mountpoint()),
+                "size mismatch");
+  mountpoint_ = static_cast<decltype(mountpoint_)>(proto.mountpoint());
+
+  scan_roots_.clear();
+  for (const auto& field : proto.scan_roots()) {
+    scan_roots_.emplace_back();
+    static_assert(sizeof(scan_roots_.back()) == sizeof(proto.scan_roots(0)),
+                  "size mismatch");
+    scan_roots_.back() = static_cast<decltype(scan_roots_)::value_type>(field);
+  }
+  unknown_fields_ = proto.unknown_fields();
+}
+
+void InodeFileConfig::MountPointMappingEntry::ToProto(
+    perfetto::protos::InodeFileConfig_MountPointMappingEntry* proto) const {
+  proto->Clear();
+
+  static_assert(sizeof(mountpoint_) == sizeof(proto->mountpoint()),
+                "size mismatch");
+  proto->set_mountpoint(
+      static_cast<decltype(proto->mountpoint())>(mountpoint_));
+
+  for (const auto& it : scan_roots_) {
+    proto->add_scan_roots(static_cast<decltype(proto->scan_roots(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->scan_roots(0)), "size mismatch");
+  }
+  *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+}  // namespace perfetto
diff --git a/src/tracing/core/null_trace_writer.cc b/src/tracing/core/null_trace_writer.cc
new file mode 100644
index 0000000..36cddf4
--- /dev/null
+++ b/src/tracing/core/null_trace_writer.cc
@@ -0,0 +1,56 @@
+/*
+ * 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 "src/tracing/core/null_trace_writer.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/utils.h"
+
+#include "perfetto/protozero/message.h"
+
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+NullTraceWriter::NullTraceWriter()
+    : delegate_(base::kPageSize), stream_(&delegate_) {
+  cur_packet_.reset(new protos::pbzero::TracePacket());
+  cur_packet_->Finalize();  // To avoid the DCHECK in NewTracePacket().
+}
+
+NullTraceWriter::~NullTraceWriter() {}
+
+void NullTraceWriter::Flush(std::function<void()> callback) {
+  // Flush() cannot be called in the middle of a TracePacket.
+  PERFETTO_CHECK(cur_packet_->is_finalized());
+
+  if (callback)
+    callback();
+}
+
+NullTraceWriter::TracePacketHandle NullTraceWriter::NewTracePacket() {
+  // If we hit this, the caller is calling NewTracePacket() without having
+  // finalized the previous packet.
+  PERFETTO_DCHECK(cur_packet_->is_finalized());
+  cur_packet_->Reset(&stream_);
+  return TraceWriter::TracePacketHandle(cur_packet_.get());
+}
+
+WriterID NullTraceWriter::writer_id() const {
+  return 0;
+}
+
+}  // namespace perfetto
diff --git a/src/tracing/core/null_trace_writer.h b/src/tracing/core/null_trace_writer.h
new file mode 100644
index 0000000..8f10819
--- /dev/null
+++ b/src/tracing/core/null_trace_writer.h
@@ -0,0 +1,54 @@
+/*
+ * 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_TRACING_CORE_NULL_TRACE_WRITER_H_
+#define SRC_TRACING_CORE_NULL_TRACE_WRITER_H_
+
+#include "perfetto/protozero/message_handle.h"
+#include "perfetto/protozero/scattered_stream_null_delegate.h"
+#include "perfetto/tracing/core/trace_writer.h"
+
+namespace perfetto {
+
+// A specialization of TraceWriter which no-ops all the writes routing them
+// into a fixed region of memory
+// See //include/perfetto/tracing/core/trace_writer.h for docs.
+class NullTraceWriter : public TraceWriter {
+ public:
+  NullTraceWriter();
+  ~NullTraceWriter() override;
+
+  // TraceWriter implementation. See documentation in trace_writer.h.
+  // TracePacketHandle is defined in trace_writer.h
+  TracePacketHandle NewTracePacket() override;
+  void Flush(std::function<void()> callback = {}) override;
+  WriterID writer_id() const override;
+
+ private:
+  NullTraceWriter(const NullTraceWriter&) = delete;
+  NullTraceWriter& operator=(const NullTraceWriter&) = delete;
+
+  protozero::ScatteredStreamWriterNullDelegate delegate_;
+  protozero::ScatteredStreamWriter stream_;
+
+  // The packet returned via NewTracePacket(). Its owned by this class,
+  // TracePacketHandle has just a pointer to it.
+  std::unique_ptr<protos::pbzero::TracePacket> cur_packet_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_CORE_NULL_TRACE_WRITER_H_
diff --git a/src/tracing/core/null_trace_writer_unittest.cc b/src/tracing/core/null_trace_writer_unittest.cc
new file mode 100644
index 0000000..f2d2f54
--- /dev/null
+++ b/src/tracing/core/null_trace_writer_unittest.cc
@@ -0,0 +1,52 @@
+/*
+ * 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 "src/tracing/core/null_trace_writer.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "perfetto/base/utils.h"
+#include "perfetto/tracing/core/trace_writer.h"
+
+#include "perfetto/trace/test_event.pbzero.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace {
+
+TEST(NullTraceWriterTest, WriterIdIsZero) {
+  NullTraceWriter writer;
+  EXPECT_EQ(writer.writer_id(), 0);
+}
+
+TEST(NullTraceWriterTest, Writing) {
+  NullTraceWriter writer;
+  for (size_t i = 0; i < 3 * base::kPageSize; i++) {
+    auto packet = writer.NewTracePacket();
+    packet->set_for_testing()->set_str("Hello, world!");
+  }
+}
+
+TEST(NullTraceWriterTest, FlushCallbackIsCalled) {
+  NullTraceWriter writer;
+  writer.Flush();
+  bool was_called = false;
+  writer.Flush([&was_called] { was_called = true; });
+  EXPECT_TRUE(was_called);
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/tracing/core/process_stats_config.cc b/src/tracing/core/process_stats_config.cc
new file mode 100644
index 0000000..fabf7d6
--- /dev/null
+++ b/src/tracing/core/process_stats_config.cc
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/process_stats/process_stats_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#include "perfetto/tracing/core/process_stats_config.h"
+
+#include "perfetto/config/process_stats/process_stats_config.pb.h"
+
+namespace perfetto {
+
+ProcessStatsConfig::ProcessStatsConfig() = default;
+ProcessStatsConfig::~ProcessStatsConfig() = default;
+ProcessStatsConfig::ProcessStatsConfig(const ProcessStatsConfig&) = default;
+ProcessStatsConfig& ProcessStatsConfig::operator=(const ProcessStatsConfig&) =
+    default;
+ProcessStatsConfig::ProcessStatsConfig(ProcessStatsConfig&&) noexcept = default;
+ProcessStatsConfig& ProcessStatsConfig::operator=(ProcessStatsConfig&&) =
+    default;
+
+void ProcessStatsConfig::FromProto(
+    const perfetto::protos::ProcessStatsConfig& proto) {
+  quirks_.clear();
+  for (const auto& field : proto.quirks()) {
+    quirks_.emplace_back();
+    static_assert(sizeof(quirks_.back()) == sizeof(proto.quirks(0)),
+                  "size mismatch");
+    quirks_.back() = static_cast<decltype(quirks_)::value_type>(field);
+  }
+  unknown_fields_ = proto.unknown_fields();
+}
+
+void ProcessStatsConfig::ToProto(
+    perfetto::protos::ProcessStatsConfig* proto) const {
+  proto->Clear();
+
+  for (const auto& it : quirks_) {
+    proto->add_quirks(static_cast<decltype(proto->quirks(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->quirks(0)), "size mismatch");
+  }
+  *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+}  // namespace perfetto
diff --git a/src/tracing/core/service_impl.cc b/src/tracing/core/service_impl.cc
index 04612ee..d7ab856 100644
--- a/src/tracing/core/service_impl.cc
+++ b/src/tracing/core/service_impl.cc
@@ -53,7 +53,6 @@
 namespace {
 constexpr size_t kDefaultShmSize = 256 * 1024ul;
 constexpr size_t kMaxShmSize = 4096 * 1024 * 512ul;
-constexpr size_t kMaxShmPageSizeKb = 16ul;
 constexpr size_t kDefaultShmPageSizeKb = base::kPageSize / 1024ul;
 constexpr int kMaxBuffersPerConsumer = 128;
 constexpr base::TimeMillis kClockSnapshotInterval(10 * 1000);
@@ -127,7 +126,7 @@
     auto next = it;
     next++;
     if (it->second.producer_id == id)
-      UnregisterDataSource(id, it->second.data_source_id);
+      UnregisterDataSource(id, it->second.descriptor.name());
     it = next;
   }
 
@@ -278,9 +277,9 @@
     const size_t buf_size_bytes = buffer_cfg.size_kb() * 1024u;
     total_buf_size_kb += buffer_cfg.size_kb();
     auto it_and_inserted =
-        buffers_.emplace(global_id, TraceBuffez::Create(buf_size_bytes));
+        buffers_.emplace(global_id, TraceBuffer::Create(buf_size_bytes));
     PERFETTO_DCHECK(it_and_inserted.second);  // buffers_.count(global_id) == 0.
-    std::unique_ptr<TraceBuffez>& trace_buffer = it_and_inserted.first->second;
+    std::unique_ptr<TraceBuffer>& trace_buffer = it_and_inserted.first->second;
     if (!trace_buffer) {
       did_allocate_all_buffers = false;
       break;
@@ -342,7 +341,7 @@
           if (weak_this)
             weak_this->ReadBuffers(tsid, nullptr);
         },
-        tracing_session->next_write_period_ms());
+        tracing_session->delay_to_next_write_period_ms());
   }
 
   tracing_session->tracing_enabled = true;
@@ -434,11 +433,18 @@
     total_slices += packet.slices().size();
   }
 
-  // This is a rough threshold to determine how to split packets within each
-  // IPC. This is not an upper bound, we just stop accumulating packets and send
-  // an IPC out every time we cross this threshold (i.e. all IPCs % last one
-  // will be >= kApproxBytesPerRead).
-  static constexpr size_t kApproxBytesPerRead = 4096;
+  // This is a rough threshold to determine how much to read from the buffer in
+  // each task. This is to avoid executing a single huge sending task for too
+  // long and risk to hit the watchdog. This is *not* an upper bound: we just
+  // stop accumulating new packets and PostTask *after* we cross this threshold.
+  // This constant essentially balances the PostTask and IPC overhead vs the
+  // responsiveness of the service. An extremely small value will cause one IPC
+  // and one PostTask for each slice but will keep the service extremely
+  // responsive. An extremely large value will batch the send for the full
+  // buffer in one large task, will hit the blocking send() once the socket
+  // buffers are full and hang the service for a bit (until the consumer
+  // catches up).
+  static constexpr size_t kApproxBytesPerTask = 32768;
   bool did_hit_threshold = false;
 
   // TODO(primiano): Extend the ReadBuffers API to allow reading only some
@@ -451,7 +457,7 @@
       PERFETTO_DCHECK(false);
       continue;
     }
-    TraceBuffez& tbuf = *tbuf_iter->second;
+    TraceBuffer& tbuf = *tbuf_iter->second;
     tbuf.BeginRead();
     while (!did_hit_threshold) {
       TracePacket packet;
@@ -486,7 +492,7 @@
       // Append the packet (inclusive of the trusted uid) to |packets|.
       packets_bytes += packet.size();
       total_slices += packet.slices().size();
-      did_hit_threshold = packets_bytes >= kApproxBytesPerRead &&
+      did_hit_threshold = packets_bytes >= kApproxBytesPerTask &&
                           !tracing_session->write_into_file;
       packets.emplace_back(std::move(packet));
     }  // for(packets...)
@@ -569,7 +575,7 @@
           if (weak_this)
             weak_this->ReadBuffers(tsid, nullptr);
         },
-        tracing_session->next_write_period_ms());
+        tracing_session->delay_to_next_write_period_ms());
     return;
   }  // if (tracing_session->write_into_file)
 
@@ -611,16 +617,14 @@
 }
 
 void ServiceImpl::RegisterDataSource(ProducerID producer_id,
-                                     DataSourceID ds_id,
                                      const DataSourceDescriptor& desc) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DLOG("Producer %" PRIu16
-                " registered data source \"%s\", ID: %" PRIu64,
-                producer_id, desc.name().c_str(), ds_id);
+  PERFETTO_DLOG("Producer %" PRIu16 " registered data source \"%s\"",
+                producer_id, desc.name().c_str());
 
   PERFETTO_DCHECK(!desc.name().empty());
-  auto reg_ds = data_sources_.emplace(
-      desc.name(), RegisteredDataSource{producer_id, ds_id, desc});
+  auto reg_ds = data_sources_.emplace(desc.name(),
+                                      RegisteredDataSource{producer_id, desc});
 
   // If there are existing tracing sessions, we need to check if the new
   // data source is enabled by any of them.
@@ -652,16 +656,15 @@
 }
 
 void ServiceImpl::UnregisterDataSource(ProducerID producer_id,
-                                       DataSourceID ds_id) {
+                                       const std::string& name) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_CHECK(producer_id);
-  PERFETTO_CHECK(ds_id);
   ProducerEndpointImpl* producer = GetProducer(producer_id);
   PERFETTO_DCHECK(producer);
   for (auto& session : tracing_sessions_) {
     auto it = session.second.data_source_instances.begin();
     while (it != session.second.data_source_instances.end()) {
-      if (it->first == producer_id && it->second.data_source_id == ds_id) {
+      if (it->first == producer_id && it->second.data_source_name == name) {
         producer->producer_->TearDownDataSourceInstance(it->second.instance_id);
         it = session.second.data_source_instances.erase(it);
       } else {
@@ -672,14 +675,16 @@
 
   for (auto it = data_sources_.begin(); it != data_sources_.end(); ++it) {
     if (it->second.producer_id == producer_id &&
-        it->second.data_source_id == ds_id) {
+        it->second.descriptor.name() == name) {
       data_sources_.erase(it);
       return;
     }
   }
-  PERFETTO_DLOG("Tried to unregister a non-existent data source %" PRIu64
-                " for producer %" PRIu16,
-                ds_id, producer_id);
+
+  PERFETTO_DLOG(
+      "Tried to unregister a non-existent data source \"%s\" for "
+      "producer %" PRIu16,
+      name.c_str(), producer_id);
   PERFETTO_DCHECK(false);
 }
 
@@ -697,8 +702,19 @@
     PERFETTO_DLOG("Lockdown mode: not enabling producer %hu", producer->id_);
     return;
   }
-  // TODO(primiano): match against |producer_name_filter| and add tests
-  // for registration ordering (data sources vs consumers).
+  // TODO(primiano): Add tests for registration ordering
+  // (data sources vs consumers).
+  if (!cfg_data_source.producer_name_filter().empty()) {
+    if (std::find(cfg_data_source.producer_name_filter().begin(),
+                  cfg_data_source.producer_name_filter().end(),
+                  producer->name_) ==
+        cfg_data_source.producer_name_filter().end()) {
+      PERFETTO_DLOG("Data source: %s is filtered out for producer: %s",
+                    cfg_data_source.config().name().c_str(),
+                    producer->name_.c_str());
+      return;
+    }
+  }
 
   // Create a copy of the DataSourceConfig specified in the trace config. This
   // will be passed to the producer after translating the |target_buffer| id.
@@ -723,14 +739,15 @@
 
   DataSourceInstanceID inst_id = ++last_data_source_instance_id_;
   tracing_session->data_source_instances.emplace(
-      producer->id_, DataSourceInstance{inst_id, data_source.data_source_id});
+      producer->id_,
+      DataSourceInstance{inst_id, data_source.descriptor.name()});
   PERFETTO_DLOG("Starting data source %s with target buffer %" PRIu16,
                 ds_config.name().c_str(), global_id);
   if (!producer->shared_memory()) {
     producer->shared_buffer_page_size_kb_ = std::min<size_t>(
         (producer_config.page_size_kb() == 0) ? kDefaultShmPageSizeKb
                                               : producer_config.page_size_kb(),
-        kMaxShmPageSizeKb);
+        SharedMemoryABI::kMaxPageSize);
 
     size_t shm_size =
         std::min<size_t>(producer_config.shm_size_kb() * 1024, kMaxShmSize);
@@ -765,7 +782,7 @@
                                                 const uint8_t* src,
                                                 size_t size) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  TraceBuffez* buf = GetBufferByID(buffer_id);
+  TraceBuffer* buf = GetBufferByID(buffer_id);
   if (!buf) {
     PERFETTO_DLOG("Could not find target buffer %" PRIu16
                   " for producer %" PRIu16,
@@ -791,7 +808,7 @@
   for (const auto& chunk : chunks_to_patch) {
     const ChunkID chunk_id = static_cast<ChunkID>(chunk.chunk_id());
     const WriterID writer_id = static_cast<WriterID>(chunk.writer_id());
-    TraceBuffez* buf =
+    TraceBuffer* buf =
         GetBufferByID(static_cast<BufferID>(chunk.target_buffer()));
     static_assert(std::numeric_limits<ChunkID>::max() == kMaxChunkID,
                   "Add a '|| chunk_id > kMaxChunkID' below if this fails");
@@ -804,7 +821,7 @@
     }
     // Speculate on the fact that there are going to be a limited amount of
     // patches per request, so we can allocate the |patches| array on the stack.
-    std::array<TraceBuffez::Patch, 1024> patches;  // Uninitialized.
+    std::array<TraceBuffer::Patch, 1024> patches;  // Uninitialized.
     if (chunk.patches().size() > patches.size()) {
       PERFETTO_DLOG("Too many patches (%zu) batched in the same request",
                     patches.size());
@@ -849,7 +866,7 @@
   return last_producer_id_;
 }
 
-TraceBuffez* ServiceImpl::GetBufferByID(BufferID buffer_id) {
+TraceBuffer* ServiceImpl::GetBufferByID(BufferID buffer_id) {
   auto buf_iter = buffers_.find(buffer_id);
   if (buf_iter == buffers_.end())
     return nullptr;
@@ -885,34 +902,35 @@
   if (now < tracing_session->last_clock_snapshot + kClockSnapshotInterval)
     return;
   tracing_session->last_clock_snapshot = now;
+
+  protos::TracePacket packet;
+  protos::ClockSnapshot* clock_snapshot = packet.mutable_clock_snapshot();
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
   struct {
     clockid_t id;
     protos::ClockSnapshot::Clock::Type type;
     struct timespec ts;
   } clocks[] = {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
-    {CLOCK_UPTIME_RAW, protos::ClockSnapshot::Clock::BOOTTIME, {0, 0}},
-#else
-    {CLOCK_BOOTTIME, protos::ClockSnapshot::Clock::BOOTTIME, {0, 0}},
-    {CLOCK_REALTIME_COARSE,
-     protos::ClockSnapshot::Clock::REALTIME_COARSE,
-     {0, 0}},
-    {CLOCK_MONOTONIC_COARSE,
-     protos::ClockSnapshot::Clock::MONOTONIC_COARSE,
-     {0, 0}},
-#endif
-    {CLOCK_REALTIME, protos::ClockSnapshot::Clock::REALTIME, {0, 0}},
-    {CLOCK_MONOTONIC, protos::ClockSnapshot::Clock::MONOTONIC, {0, 0}},
-    {CLOCK_MONOTONIC_RAW, protos::ClockSnapshot::Clock::MONOTONIC_RAW, {0, 0}},
-    {CLOCK_PROCESS_CPUTIME_ID,
-     protos::ClockSnapshot::Clock::PROCESS_CPUTIME,
-     {0, 0}},
-    {CLOCK_THREAD_CPUTIME_ID,
-     protos::ClockSnapshot::Clock::THREAD_CPUTIME,
-     {0, 0}},
+      {CLOCK_BOOTTIME, protos::ClockSnapshot::Clock::BOOTTIME, {0, 0}},
+      {CLOCK_REALTIME_COARSE,
+       protos::ClockSnapshot::Clock::REALTIME_COARSE,
+       {0, 0}},
+      {CLOCK_MONOTONIC_COARSE,
+       protos::ClockSnapshot::Clock::MONOTONIC_COARSE,
+       {0, 0}},
+      {CLOCK_REALTIME, protos::ClockSnapshot::Clock::REALTIME, {0, 0}},
+      {CLOCK_MONOTONIC, protos::ClockSnapshot::Clock::MONOTONIC, {0, 0}},
+      {CLOCK_MONOTONIC_RAW,
+       protos::ClockSnapshot::Clock::MONOTONIC_RAW,
+       {0, 0}},
+      {CLOCK_PROCESS_CPUTIME_ID,
+       protos::ClockSnapshot::Clock::PROCESS_CPUTIME,
+       {0, 0}},
+      {CLOCK_THREAD_CPUTIME_ID,
+       protos::ClockSnapshot::Clock::THREAD_CPUTIME,
+       {0, 0}},
   };
-  protos::TracePacket packet;
-  protos::ClockSnapshot* clock_snapshot = packet.mutable_clock_snapshot();
   // First snapshot all the clocks as atomically as we can.
   for (auto& clock : clocks) {
     if (clock_gettime(clock.id, &clock.ts) == -1)
@@ -923,6 +941,12 @@
     c->set_type(clock.type);
     c->set_timestamp(base::FromPosixTimespec(clock.ts).count());
   }
+#else   // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+  protos::ClockSnapshot::Clock* c = clock_snapshot->add_clocks();
+  c->set_type(protos::ClockSnapshot::Clock::MONOTONIC);
+  c->set_timestamp(base::GetWallTimeNs().count());
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+
   packet.set_trusted_uid(getuid());
   Slice slice = Slice::Allocate(packet.ByteSize());
   PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
@@ -1040,24 +1064,20 @@
 }
 
 void ServiceImpl::ProducerEndpointImpl::RegisterDataSource(
-    const DataSourceDescriptor& desc,
-    RegisterDataSourceCallback callback) {
+    const DataSourceDescriptor& desc) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  DataSourceID ds_id = ++last_data_source_id_;
-  if (!desc.name().empty()) {
-    service_->RegisterDataSource(id_, ds_id, desc);
-  } else {
+  if (desc.name().empty()) {
     PERFETTO_DLOG("Received RegisterDataSource() with empty name");
-    ds_id = 0;
+    return;
   }
-  task_runner_->PostTask(std::bind(std::move(callback), ds_id));
+
+  service_->RegisterDataSource(id_, desc);
 }
 
 void ServiceImpl::ProducerEndpointImpl::UnregisterDataSource(
-    DataSourceID ds_id) {
+    const std::string& name) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_CHECK(ds_id);
-  service_->UnregisterDataSource(id_, ds_id);
+  service_->UnregisterDataSource(id_, name);
 }
 
 void ServiceImpl::ProducerEndpointImpl::CommitData(
diff --git a/src/tracing/core/service_impl.h b/src/tracing/core/service_impl.h
index 4f7428c..baf9f73 100644
--- a/src/tracing/core/service_impl.h
+++ b/src/tracing/core/service_impl.h
@@ -46,7 +46,7 @@
 class Producer;
 class SharedMemory;
 class SharedMemoryArbiterImpl;
-class TraceBuffez;
+class TraceBuffer;
 class TraceConfig;
 class TracePacket;
 
@@ -65,9 +65,8 @@
     ~ProducerEndpointImpl() override;
 
     // Service::ProducerEndpoint implementation.
-    void RegisterDataSource(const DataSourceDescriptor&,
-                            RegisterDataSourceCallback) override;
-    void UnregisterDataSource(DataSourceID) override;
+    void RegisterDataSource(const DataSourceDescriptor&) override;
+    void UnregisterDataSource(const std::string& name) override;
     void CommitData(const CommitDataRequest&, CommitDataCallback) override;
     void SetSharedMemory(std::unique_ptr<SharedMemory>);
 
@@ -90,7 +89,6 @@
     size_t shared_buffer_page_size_kb_ = 0;
     SharedMemoryABI shmem_abi_;
     size_t shared_memory_size_hint_bytes_ = 0;
-    DataSourceID last_data_source_id_ = 0;
     const std::string name_;
 
     // This is used only in in-process configurations (mostly tests).
@@ -134,10 +132,8 @@
 
   // Called by ProducerEndpointImpl.
   void DisconnectProducer(ProducerID);
-  void RegisterDataSource(ProducerID,
-                          DataSourceID,
-                          const DataSourceDescriptor&);
-  void UnregisterDataSource(ProducerID, DataSourceID);
+  void RegisterDataSource(ProducerID, const DataSourceDescriptor&);
+  void UnregisterDataSource(ProducerID, const std::string& name);
   void CopyProducerPageIntoLogBuffer(ProducerID,
                                      uid_t,
                                      WriterID,
@@ -178,14 +174,13 @@
 
   struct RegisteredDataSource {
     ProducerID producer_id;
-    DataSourceID data_source_id;
     DataSourceDescriptor descriptor;
   };
 
   // Represents an active data source for a tracing session.
   struct DataSourceInstance {
     DataSourceInstanceID instance_id;
-    DataSourceID data_source_id;
+    std::string data_source_name;
   };
 
   // Holds the state of a tracing session. A tracing session is uniquely bound
@@ -195,10 +190,10 @@
 
     size_t num_buffers() const { return buffers_index.size(); }
 
-    int next_write_period_ms() const {
-      PERFETTO_DCHECK(write_period_ms);
-      // TODO(primiano): this will drift. Synchronize % period so it aligns.
-      return write_period_ms;
+    int delay_to_next_write_period_ms() const {
+      PERFETTO_DCHECK(write_period_ms > 0);
+      return write_period_ms -
+             (base::GetWallTimeMs().count() % write_period_ms);
     }
 
     // The consumer that started the session.
@@ -257,7 +252,7 @@
   void MaybeSnapshotClocks(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
 
-  TraceBuffez* GetBufferByID(BufferID);
+  TraceBuffer* GetBufferByID(BufferID);
 
   base::TaskRunner* const task_runner_;
   std::unique_ptr<SharedMemory::Factory> shm_factory_;
@@ -278,7 +273,7 @@
 
   std::set<ConsumerEndpointImpl*> consumers_;
   std::map<TracingSessionID, TracingSession> tracing_sessions_;
-  std::map<BufferID, std::unique_ptr<TraceBuffez>> buffers_;
+  std::map<BufferID, std::unique_ptr<TraceBuffer>> buffers_;
 
   bool lockdown_mode_ = false;
 
diff --git a/src/tracing/core/service_impl_unittest.cc b/src/tracing/core/service_impl_unittest.cc
index a10dea9..77256ac 100644
--- a/src/tracing/core/service_impl_unittest.cc
+++ b/src/tracing/core/service_impl_unittest.cc
@@ -110,23 +110,16 @@
 
   DataSourceDescriptor ds_desc1;
   ds_desc1.set_name("foo");
-  producer_endpoint_1->RegisterDataSource(
-      ds_desc1, [this, &producer_endpoint_1](DataSourceID id) {
-        EXPECT_EQ(1u, id);
-        task_runner.PostTask(
-            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
-                      producer_endpoint_1.get(), id));
-      });
+  producer_endpoint_1->RegisterDataSource(ds_desc1);
 
   DataSourceDescriptor ds_desc2;
   ds_desc2.set_name("bar");
-  producer_endpoint_2->RegisterDataSource(
-      ds_desc2, [this, &producer_endpoint_2](DataSourceID id) {
-        EXPECT_EQ(1u, id);
-        task_runner.PostTask(
-            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
-                      producer_endpoint_2.get(), id));
-      });
+  producer_endpoint_2->RegisterDataSource(ds_desc2);
+
+  task_runner.RunUntilIdle();
+
+  producer_endpoint_1->UnregisterDataSource("foo");
+  producer_endpoint_2->UnregisterDataSource("bar");
 
   task_runner.RunUntilIdle();
 
@@ -161,7 +154,7 @@
 
   DataSourceDescriptor ds_desc;
   ds_desc.set_name("foo");
-  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
+  producer_endpoint->RegisterDataSource(ds_desc);
 
   task_runner.RunUntilIdle();
 
@@ -247,7 +240,7 @@
 
   DataSourceDescriptor ds_desc;
   ds_desc.set_name("foo");
-  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
+  producer_endpoint->RegisterDataSource(ds_desc);
   task_runner.RunUntilIdle();
 
   // Disconnecting the consumer while tracing should trigger data source
@@ -287,7 +280,7 @@
 
   DataSourceDescriptor ds_desc;
   ds_desc.set_name("foo");
-  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
+  producer_endpoint->RegisterDataSource(ds_desc);
   task_runner.RunUntilIdle();
 
   // Disconnecting the producer while tracing should trigger data source
@@ -312,7 +305,7 @@
   task_runner.RunUntilIdle();
   EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
   EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
-  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
+  producer_endpoint->RegisterDataSource(ds_desc);
   task_runner.RunUntilIdle();
 
   EXPECT_CALL(mock_consumer, OnDisconnect());
@@ -403,7 +396,7 @@
 
   DataSourceDescriptor ds_desc;
   ds_desc.set_name("datasource");
-  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
+  producer_endpoint->RegisterDataSource(ds_desc);
   task_runner.RunUntilIdle();
 
   static const char kPayload[] = "1234567890abcdef-";
diff --git a/src/tracing/core/shared_memory_abi.cc b/src/tracing/core/shared_memory_abi.cc
index 70021a8..9d2964a 100644
--- a/src/tracing/core/shared_memory_abi.cc
+++ b/src/tracing/core/shared_memory_abi.cc
@@ -23,6 +23,17 @@
 namespace perfetto {
 
 namespace {
+
+constexpr int kRetryAttempts = 64;
+
+inline void WaitBeforeNextAttempt(int attempt) {
+  if (attempt < kRetryAttempts / 2) {
+    std::this_thread::yield();
+  } else {
+    usleep((attempt / 10) * 1000);
+  }
+}
+
 // Returns the largest 4-bytes aligned chunk size <= |page_size| / |divider|
 // for each divider in PageLayout.
 constexpr size_t GetChunkSize(size_t page_size, size_t divider) {
@@ -51,6 +62,7 @@
 constexpr size_t SharedMemoryABI::kNumChunksForLayout[];
 constexpr const char* SharedMemoryABI::kChunkStateStr[];
 constexpr const size_t SharedMemoryABI::kInvalidPageIdx;
+constexpr const size_t SharedMemoryABI::kMaxPageSize;
 
 SharedMemoryABI::SharedMemoryABI() = default;
 
@@ -109,9 +121,10 @@
   chunk_header.writer_id = -1;
   PERFETTO_CHECK(kMaxWriterID <= chunk_header.writer_id);
 
-  PERFETTO_CHECK(page_size >= 4096);
-  PERFETTO_CHECK(page_size % 4096 == 0);
-  PERFETTO_CHECK(reinterpret_cast<uintptr_t>(start) % 4096 == 0);
+  PERFETTO_CHECK(page_size >= base::kPageSize);
+  PERFETTO_CHECK(page_size <= kMaxPageSize);
+  PERFETTO_CHECK(page_size % base::kPageSize == 0);
+  PERFETTO_CHECK(reinterpret_cast<uintptr_t>(start) % base::kPageSize == 0);
   PERFETTO_CHECK(size % page_size == 0);
 }
 
@@ -138,45 +151,44 @@
   PERFETTO_DCHECK(desired_chunk_state == kChunkBeingRead ||
                   desired_chunk_state == kChunkBeingWritten);
   PageHeader* phdr = page_header(page_idx);
-  uint32_t layout = phdr->layout.load(std::memory_order_acquire);
-  const size_t num_chunks = GetNumChunksForLayout(layout);
+  for (int attempt = 0; attempt < kRetryAttempts; attempt++) {
+    uint32_t layout = phdr->layout.load(std::memory_order_acquire);
+    const size_t num_chunks = GetNumChunksForLayout(layout);
 
-  // The page layout has changed (or the page is free).
-  if (chunk_idx >= num_chunks)
-    return Chunk();
+    // The page layout has changed (or the page is free).
+    if (chunk_idx >= num_chunks)
+      return Chunk();
 
-  // Verify that the chunk is still in a state that allows the transition to
-  // |desired_chunk_state|. The only allowed transitions are:
-  // 1. kChunkFree -> kChunkBeingWritten (Producer).
-  // 2. kChunkComplete -> kChunkBeingRead (Service).
-  ChunkState expected_chunk_state =
-      desired_chunk_state == kChunkBeingWritten ? kChunkFree : kChunkComplete;
-  auto cur_chunk_state = (layout >> (chunk_idx * kChunkShift)) & kChunkMask;
-  if (cur_chunk_state != expected_chunk_state)
-    return Chunk();
+    // Verify that the chunk is still in a state that allows the transition to
+    // |desired_chunk_state|. The only allowed transitions are:
+    // 1. kChunkFree -> kChunkBeingWritten (Producer).
+    // 2. kChunkComplete -> kChunkBeingRead (Service).
+    ChunkState expected_chunk_state =
+        desired_chunk_state == kChunkBeingWritten ? kChunkFree : kChunkComplete;
+    auto cur_chunk_state = (layout >> (chunk_idx * kChunkShift)) & kChunkMask;
+    if (cur_chunk_state != expected_chunk_state)
+      return Chunk();
 
-  uint32_t next_layout = layout;
-  next_layout &= ~(kChunkMask << (chunk_idx * kChunkShift));
-  next_layout |= (desired_chunk_state << (chunk_idx * kChunkShift));
-  if (!phdr->layout.compare_exchange_strong(layout, next_layout,
-                                            std::memory_order_acq_rel)) {
-    // TODO(fmayer): returning here is too pessimistic. We should look at the
-    // returned |layout| to figure out if some other writer thread took the same
-    // chunk (in which case we should immediately return false) or if they took
-    // another chunk in the same page (in which case we should just retry).
-    return Chunk();
+    uint32_t next_layout = layout;
+    next_layout &= ~(kChunkMask << (chunk_idx * kChunkShift));
+    next_layout |= (desired_chunk_state << (chunk_idx * kChunkShift));
+    if (phdr->layout.compare_exchange_strong(layout, next_layout,
+                                             std::memory_order_acq_rel)) {
+      // Compute the chunk virtual address and write it into |chunk|.
+      Chunk chunk = GetChunkUnchecked(page_idx, layout, chunk_idx);
+      if (desired_chunk_state == kChunkBeingWritten) {
+        PERFETTO_DCHECK(header);
+        ChunkHeader* new_header = chunk.header();
+        new_header->packets.store(header->packets, std::memory_order_relaxed);
+        new_header->writer_id.store(header->writer_id,
+                                    std::memory_order_relaxed);
+        new_header->chunk_id.store(header->chunk_id, std::memory_order_release);
+      }
+      return chunk;
+    }
+    WaitBeforeNextAttempt(attempt);
   }
-
-  // Compute the chunk virtual address and write it into |chunk|.
-  Chunk chunk = GetChunkUnchecked(page_idx, layout, chunk_idx);
-  if (desired_chunk_state == kChunkBeingWritten) {
-    PERFETTO_DCHECK(header);
-    ChunkHeader* new_header = chunk.header();
-    new_header->packets.store(header->packets, std::memory_order_relaxed);
-    new_header->writer_id.store(header->writer_id, std::memory_order_relaxed);
-    new_header->chunk_id.store(header->chunk_id, std::memory_order_release);
-  }
-  return chunk;
+  return Chunk();  // All our attempts failed.
 }
 
 bool SharedMemoryABI::TryPartitionPage(size_t page_idx, PageLayout layout) {
@@ -212,7 +224,7 @@
   size_t chunk_idx;
   std::tie(page_idx, chunk_idx) = GetPageAndChunkIndex(chunk);
 
-  for (int attempt = 0; attempt < 64; attempt++) {
+  for (int attempt = 0; attempt < kRetryAttempts; attempt++) {
     PageHeader* phdr = page_header(page_idx);
     uint32_t layout = phdr->layout.load(std::memory_order_relaxed);
     const size_t page_chunk_size = GetChunkSizeForLayout(layout);
@@ -250,11 +262,7 @@
                                              std::memory_order_acq_rel)) {
       return page_idx;
     }
-    if (attempt < 32) {
-      std::this_thread::yield();
-    } else {
-      usleep((attempt / 10) * 1000);
-    }
+    WaitBeforeNextAttempt(attempt);
   }
   // Too much contention on this page. Give up. This page will be left pending
   // forever but there isn't much more we can do at this point.
diff --git a/src/tracing/core/shared_memory_arbiter_impl.cc b/src/tracing/core/shared_memory_arbiter_impl.cc
index c1b7e23..b6c6fd9 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl.cc
@@ -20,6 +20,7 @@
 #include "perfetto/base/task_runner.h"
 #include "perfetto/tracing/core/commit_data_request.h"
 #include "perfetto/tracing/core/shared_memory.h"
+#include "src/tracing/core/null_trace_writer.h"
 #include "src/tracing/core/trace_writer_impl.h"
 
 #include <limits>
@@ -214,7 +215,6 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
   std::unique_ptr<CommitDataRequest> req;
-  std::vector<uint32_t> pages_to_notify;
   {
     std::lock_guard<std::mutex> scoped_lock(lock_);
     req = std::move(commit_data_req_);
@@ -240,8 +240,10 @@
     std::lock_guard<std::mutex> scoped_lock(lock_);
     id = active_writer_ids_.Allocate();
   }
+  if (!id)
+    return std::unique_ptr<TraceWriter>(new NullTraceWriter());
   return std::unique_ptr<TraceWriter>(
-      id ? new TraceWriterImpl(this, id, target_buffer) : nullptr);
+      new TraceWriterImpl(this, id, target_buffer));
 }
 
 void SharedMemoryArbiterImpl::ReleaseWriterID(WriterID id) {
diff --git a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
index 8505876..ed96226 100644
--- a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
@@ -35,9 +35,8 @@
 
 class MockProducerEndpoint : public Service::ProducerEndpoint {
  public:
-  void RegisterDataSource(const DataSourceDescriptor&,
-                          RegisterDataSourceCallback) override {}
-  void UnregisterDataSource(DataSourceID) override {}
+  void RegisterDataSource(const DataSourceDescriptor&) override {}
+  void UnregisterDataSource(const std::string&) override {}
   SharedMemory* shared_memory() const override { return nullptr; }
   size_t shared_buffer_page_size_kb() const override { return 0; }
   std::unique_ptr<TraceWriter> CreateTraceWriter(BufferID) override {
@@ -137,8 +136,9 @@
     ASSERT_TRUE(writers.emplace(writer_id, std::move(writer)).second);
   }
 
-  // A further call should fail as we exhausted writer IDs.
-  ASSERT_EQ(nullptr, arbiter_->CreateTraceWriter(0).get());
+  // A further call should return a null impl of trace writer as we exhausted
+  // writer IDs.
+  ASSERT_EQ(arbiter_->CreateTraceWriter(0)->writer_id(), 0);
 }
 
 // TODO(primiano): add multi-threaded tests.
diff --git a/src/tracing/core/test_config.cc b/src/tracing/core/test_config.cc
index e352ec3..d58cbce 100644
--- a/src/tracing/core/test_config.cc
+++ b/src/tracing/core/test_config.cc
@@ -43,6 +43,12 @@
                 "size mismatch");
   message_count_ = static_cast<decltype(message_count_)>(proto.message_count());
 
+  static_assert(sizeof(max_messages_per_second_) ==
+                    sizeof(proto.max_messages_per_second()),
+                "size mismatch");
+  max_messages_per_second_ = static_cast<decltype(max_messages_per_second_)>(
+      proto.max_messages_per_second());
+
   static_assert(sizeof(seed_) == sizeof(proto.seed()), "size mismatch");
   seed_ = static_cast<decltype(seed_)>(proto.seed());
 
@@ -60,6 +66,13 @@
   proto->set_message_count(
       static_cast<decltype(proto->message_count())>(message_count_));
 
+  static_assert(sizeof(max_messages_per_second_) ==
+                    sizeof(proto->max_messages_per_second()),
+                "size mismatch");
+  proto->set_max_messages_per_second(
+      static_cast<decltype(proto->max_messages_per_second())>(
+          max_messages_per_second_));
+
   static_assert(sizeof(seed_) == sizeof(proto->seed()), "size mismatch");
   proto->set_seed(static_cast<decltype(proto->seed())>(seed_));
 
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 1a7a554..fb5b7b3 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -59,27 +59,27 @@
     SharedMemoryABI::ChunkHeader::kChunkNeedsPatching;
 }  // namespace.
 
-constexpr size_t TraceBuffez::ChunkRecord::kMaxSize;
-constexpr size_t TraceBuffez::InlineChunkHeaderSize = sizeof(ChunkRecord);
+constexpr size_t TraceBuffer::ChunkRecord::kMaxSize;
+constexpr size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
 
 // static
-std::unique_ptr<TraceBuffez> TraceBuffez::Create(size_t size_in_bytes) {
-  std::unique_ptr<TraceBuffez> trace_buffer(new TraceBuffez());
+std::unique_ptr<TraceBuffer> TraceBuffer::Create(size_t size_in_bytes) {
+  std::unique_ptr<TraceBuffer> trace_buffer(new TraceBuffer());
   if (!trace_buffer->Initialize(size_in_bytes))
     return nullptr;
   return trace_buffer;
 }
 
-TraceBuffez::TraceBuffez() {
+TraceBuffer::TraceBuffer() {
   // See comments in ChunkRecord for the rationale of this.
   static_assert(sizeof(ChunkRecord) == sizeof(SharedMemoryABI::PageHeader) +
                                            sizeof(SharedMemoryABI::ChunkHeader),
                 "ChunkRecord out of sync with the layout of SharedMemoryABI");
 }
 
-TraceBuffez::~TraceBuffez() = default;
+TraceBuffer::~TraceBuffer() = default;
 
-bool TraceBuffez::Initialize(size_t size) {
+bool TraceBuffer::Initialize(size_t size) {
   static_assert(
       base::kPageSize % sizeof(ChunkRecord) == 0,
       "sizeof(ChunkRecord) must be an integer divider of a page size");
@@ -101,7 +101,7 @@
 // Note: |src| points to a shmem region that is shared with the producer. Assume
 // that the producer is malicious and will change the content of |src|
 // while we execute here. Don't do any processing on it other than memcpy().
-void TraceBuffez::CopyChunkUntrusted(ProducerID producer_id_trusted,
+void TraceBuffer::CopyChunkUntrusted(ProducerID producer_id_trusted,
                                      uid_t producer_uid_trusted,
                                      WriterID writer_id,
                                      ChunkID chunk_id,
@@ -195,7 +195,7 @@
     AddPaddingRecord(padding_size);
 }
 
-size_t TraceBuffez::DeleteNextChunksFor(size_t bytes_to_clear) {
+size_t TraceBuffer::DeleteNextChunksFor(size_t bytes_to_clear) {
   // Find the position of the first chunk which begins at or after
   // (|wptr_| + |bytes|). Note that such a chunk might not exist and we might
   // either reach the end of the buffer or a zeroed region of the buffer.
@@ -247,7 +247,7 @@
   return next_chunk_ptr - search_end;
 }
 
-void TraceBuffez::AddPaddingRecord(size_t size) {
+void TraceBuffer::AddPaddingRecord(size_t size) {
   PERFETTO_DCHECK(size >= sizeof(ChunkRecord) && size <= ChunkRecord::kMaxSize);
   ChunkRecord record(size);
   record.is_padding = 1;
@@ -257,7 +257,7 @@
   // |wptr_| is deliberately not advanced when writing a padding record.
 }
 
-bool TraceBuffez::TryPatchChunkContents(ProducerID producer_id,
+bool TraceBuffer::TryPatchChunkContents(ProducerID producer_id,
                                         WriterID writer_id,
                                         ChunkID chunk_id,
                                         const Patch* patches,
@@ -320,14 +320,14 @@
   return true;
 }
 
-void TraceBuffez::BeginRead() {
+void TraceBuffer::BeginRead() {
   read_iter_ = GetReadIterForSequence(index_.begin());
 #if PERFETTO_DCHECK_IS_ON()
   changed_since_last_read_ = false;
 #endif
 }
 
-TraceBuffez::SequenceIterator TraceBuffez::GetReadIterForSequence(
+TraceBuffer::SequenceIterator TraceBuffer::GetReadIterForSequence(
     ChunkMap::iterator seq_begin) {
   SequenceIterator iter;
   iter.seq_begin = seq_begin;
@@ -369,7 +369,7 @@
   return iter;
 }
 
-void TraceBuffez::SequenceIterator::MoveNext() {
+void TraceBuffer::SequenceIterator::MoveNext() {
   // Note: |seq_begin| might be == |seq_end|.
   if (cur == seq_end || cur->first.chunk_id == wrapping_id) {
     cur = seq_end;
@@ -379,7 +379,7 @@
     cur = seq_begin;
 }
 
-bool TraceBuffez::ReadNextTracePacket(TracePacket* packet,
+bool TraceBuffer::ReadNextTracePacket(TracePacket* packet,
                                       uid_t* producer_uid) {
   // Note: MoveNext() moves only within the next chunk within the same
   // {ProducerID, WriterID} sequence. Here we want to:
@@ -523,7 +523,7 @@
   }    // for(;;MoveNext()) [iterate over chunks].
 }
 
-TraceBuffez::ReadAheadResult TraceBuffez::ReadAhead(TracePacket* packet) {
+TraceBuffer::ReadAheadResult TraceBuffer::ReadAhead(TracePacket* packet) {
   static_assert(static_cast<ChunkID>(kMaxChunkID + 1) == 0,
                 "relying on kMaxChunkID to wrap naturally");
   TRACE_BUFFER_DLOG(" readahead start @ chunk %u", read_iter_.chunk_id());
@@ -600,7 +600,7 @@
   return ReadAheadResult::kFailedMoveToNextSequence;
 }
 
-bool TraceBuffez::ReadNextPacketInChunk(ChunkMeta* chunk_meta,
+bool TraceBuffer::ReadNextPacketInChunk(ChunkMeta* chunk_meta,
                                         TracePacket* packet) {
   PERFETTO_DCHECK(chunk_meta->num_fragments_read < chunk_meta->num_fragments);
   PERFETTO_DCHECK(!(chunk_meta->flags & kChunkNeedsPatching));
diff --git a/src/tracing/core/trace_buffer.h b/src/tracing/core/trace_buffer.h
index 2aea918..a66a4db 100644
--- a/src/tracing/core/trace_buffer.h
+++ b/src/tracing/core/trace_buffer.h
@@ -123,10 +123,7 @@
 // Reads guarantee that packets for the same sequence are read in FIFO order
 // (according to their ChunkID), but don't give any guarantee about the read
 // order of packets from different sequences (see ReadPacket() comments below).
-//
-// TODO(primiano): the name of this class is deliberately typo-ed as a temporary
-// situation until we replace TraceBuffer within service_impl.cc.
-class TraceBuffez {
+class TraceBuffer {
  public:
   static const size_t InlineChunkHeaderSize;  // For test/fake_packet.{cc,h}.
 
@@ -151,9 +148,9 @@
   };
 
   // Can return nullptr if the memory allocation fails.
-  static std::unique_ptr<TraceBuffez> Create(size_t size_in_bytes);
+  static std::unique_ptr<TraceBuffer> Create(size_t size_in_bytes);
 
-  ~TraceBuffez();
+  ~TraceBuffer();
 
   // Copies a Chunk from a producer Shared Memory Buffer into the trace buffer.
   // |src| points to the first packet in the SharedMemoryABI's chunk shared
@@ -399,9 +396,9 @@
     kFailedStayOnSameSequence,
   };
 
-  TraceBuffez();
-  TraceBuffez(const TraceBuffez&) = delete;
-  TraceBuffez& operator=(const TraceBuffez&) = delete;
+  TraceBuffer();
+  TraceBuffer(const TraceBuffer&) = delete;
+  TraceBuffer& operator=(const TraceBuffer&) = delete;
 
   bool Initialize(size_t size);
 
diff --git a/src/tracing/core/trace_buffer_unittest.cc b/src/tracing/core/trace_buffer_unittest.cc
index c12c8e3..3031b3b 100644
--- a/src/tracing/core/trace_buffer_unittest.cc
+++ b/src/tracing/core/trace_buffer_unittest.cc
@@ -39,9 +39,9 @@
 
 class TraceBufferTest : public testing::Test {
  public:
-  using SequenceIterator = TraceBuffez::SequenceIterator;
-  using ChunkMetaKey = TraceBuffez::ChunkMeta::Key;
-  using ChunkRecord = TraceBuffez::ChunkRecord;
+  using SequenceIterator = TraceBuffer::SequenceIterator;
+  using ChunkMetaKey = TraceBuffer::ChunkMeta::Key;
+  using ChunkRecord = TraceBuffer::ChunkRecord;
 
   static constexpr uint8_t kContFromPrevChunk =
       SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk;
@@ -55,14 +55,14 @@
   }
 
   void ResetBuffer(size_t size_) {
-    trace_buffer_ = TraceBuffez::Create(size_);
+    trace_buffer_ = TraceBuffer::Create(size_);
     ASSERT_TRUE(trace_buffer_);
   }
 
   bool TryPatchChunkContents(ProducerID p,
                              WriterID w,
                              ChunkID c,
-                             std::vector<TraceBuffez::Patch> patches,
+                             std::vector<TraceBuffer::Patch> patches,
                              bool other_patches_pending = false) {
     return trace_buffer_->TryPatchChunkContents(
         p, w, c, patches.data(), patches.size(), other_patches_pending);
@@ -109,7 +109,7 @@
   }
 
   SequenceIterator GetReadIterForSequence(ProducerID p, WriterID w) {
-    TraceBuffez::ChunkMeta::Key key(p, w, 0);
+    TraceBuffer::ChunkMeta::Key key(p, w, 0);
     return trace_buffer_->GetReadIterForSequence(
         trace_buffer_->index_.lower_bound(key));
   }
@@ -126,11 +126,11 @@
     return keys;
   }
 
-  TraceBuffez* trace_buffer() { return trace_buffer_.get(); }
+  TraceBuffer* trace_buffer() { return trace_buffer_.get(); }
   size_t size_to_end() { return trace_buffer_->size_to_end(); }
 
  private:
-  std::unique_ptr<TraceBuffez> trace_buffer_;
+  std::unique_ptr<TraceBuffer> trace_buffer_;
 };
 
 // ----------------------
diff --git a/src/tracing/core/trace_config.cc b/src/tracing/core/trace_config.cc
index 68d371b..f3bfca5 100644
--- a/src/tracing/core/trace_config.cc
+++ b/src/tracing/core/trace_config.cc
@@ -220,7 +220,8 @@
   config_.ToProto(proto->mutable_config());
 
   for (const auto& it : producer_name_filter_) {
-    proto->add_producer_name_filter(it);
+    proto->add_producer_name_filter(
+        static_cast<decltype(proto->producer_name_filter(0))>(it));
     static_assert(sizeof(it) == sizeof(proto->producer_name_filter(0)),
                   "size mismatch");
   }
diff --git a/src/tracing/core/trace_packet.cc b/src/tracing/core/trace_packet.cc
index 9ebcc47..5e72695 100644
--- a/src/tracing/core/trace_packet.cc
+++ b/src/tracing/core/trace_packet.cc
@@ -28,8 +28,20 @@
 TracePacket::TracePacket() = default;
 TracePacket::~TracePacket() = default;
 
-TracePacket::TracePacket(TracePacket&&) noexcept = default;
-TracePacket& TracePacket::operator=(TracePacket&&) = default;
+TracePacket::TracePacket(TracePacket&& other) noexcept {
+  *this = std::move(other);
+}
+
+TracePacket& TracePacket::operator=(TracePacket&& other) {
+  slices_ = std::move(other.slices_);
+  other.slices_.clear();
+
+  size_ = other.size_;
+  other.size_ = 0;
+
+  decoded_packet_ = std::move(other.decoded_packet_);
+  return *this;
+}
 
 bool TracePacket::Decode() {
   if (decoded_packet_)
diff --git a/src/tracing/core/trace_packet_unittest.cc b/src/tracing/core/trace_packet_unittest.cc
index ef5c41b..9e3e4ed 100644
--- a/src/tracing/core/trace_packet_unittest.cc
+++ b/src/tracing/core/trace_packet_unittest.cc
@@ -43,16 +43,6 @@
   ASSERT_NE(nullptr, tp.operator->());
   ASSERT_EQ(proto.for_testing().str(), tp->for_testing().str());
   ASSERT_EQ(proto.for_testing().str(), (*tp).for_testing().str());
-
-  // Check move operators.
-  TracePacket moved_tp(std::move(tp));
-  ASSERT_NE(nullptr, moved_tp.operator->());
-  ASSERT_EQ(proto.for_testing().str(), moved_tp->for_testing().str());
-
-  TracePacket moved_tp_2;
-  moved_tp_2 = std::move(moved_tp);
-  ASSERT_NE(nullptr, moved_tp_2.operator->());
-  ASSERT_EQ(proto.for_testing().str(), moved_tp_2->for_testing().str());
 }
 
 TEST(TracePacketTest, Sliced) {
@@ -131,5 +121,29 @@
   ASSERT_EQ(payload, trace.packet(0).for_testing().str());
 }
 
+TEST(TracePacketTest, MoveOperators) {
+  char buf1[5]{};
+  char buf2[7]{};
+
+  TracePacket tp;
+  tp.AddSlice(buf1, sizeof(buf1));
+  tp.AddSlice(buf2, sizeof(buf2));
+  tp.AddSlice(Slice::Allocate(11));
+  tp.AddSlice(Slice(std::unique_ptr<std::string>(new std::string("foobar"))));
+
+  TracePacket moved_tp(std::move(tp));
+  ASSERT_EQ(0u, tp.size());
+  ASSERT_TRUE(tp.slices().empty());
+  ASSERT_EQ(4u, moved_tp.slices().size());
+  ASSERT_EQ(5u + 7u + 11u + 6u, moved_tp.size());
+
+  TracePacket moved_tp_2;
+  moved_tp_2 = std::move(moved_tp);
+  ASSERT_EQ(0u, moved_tp.size());
+  ASSERT_TRUE(moved_tp.slices().empty());
+  ASSERT_EQ(4u, moved_tp_2.slices().size());
+  ASSERT_EQ(5u + 7u + 11u + 6u, moved_tp_2.size());
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_impl.cc b/src/tracing/core/trace_writer_impl.cc
index ded5909..bfdf1f4 100644
--- a/src/tracing/core/trace_writer_impl.cc
+++ b/src/tracing/core/trace_writer_impl.cc
@@ -197,7 +197,7 @@
 
 WriterID TraceWriterImpl::writer_id() const {
   return id_;
-};
+}
 
 // Base class ctor/dtor definition.
 TraceWriter::TraceWriter() = default;
diff --git a/src/tracing/core/trace_writer_impl.h b/src/tracing/core/trace_writer_impl.h
index 28fa95f..3ba878d 100644
--- a/src/tracing/core/trace_writer_impl.h
+++ b/src/tracing/core/trace_writer_impl.h
@@ -36,7 +36,7 @@
   TraceWriterImpl(SharedMemoryArbiterImpl*, WriterID, BufferID);
   ~TraceWriterImpl() override;
 
-  // TraceWriter implementation. See documentation in trace_writer.h .
+  // TraceWriter implementation. See documentation in trace_writer.h.
   TracePacketHandle NewTracePacket() override;
   void Flush(std::function<void()> callback = {}) override;
   WriterID writer_id() const override;
diff --git a/src/tracing/core/trace_writer_impl_unittest.cc b/src/tracing/core/trace_writer_impl_unittest.cc
index 5acf403..ad9156a 100644
--- a/src/tracing/core/trace_writer_impl_unittest.cc
+++ b/src/tracing/core/trace_writer_impl_unittest.cc
@@ -32,9 +32,8 @@
 namespace {
 
 class FakeProducerEndpoint : public Service::ProducerEndpoint {
-  void RegisterDataSource(const DataSourceDescriptor&,
-                          RegisterDataSourceCallback) override {}
-  void UnregisterDataSource(DataSourceID) override {}
+  void RegisterDataSource(const DataSourceDescriptor&) override {}
+  void UnregisterDataSource(const std::string&) override {}
   void CommitData(const CommitDataRequest&, CommitDataCallback) override {}
   SharedMemory* shared_memory() const override { return nullptr; }
   size_t shared_buffer_page_size_kb() const override { return 0; }
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
index a0db4e5..c671779 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
@@ -23,7 +23,6 @@
 #include "perfetto/ipc/client.h"
 #include "perfetto/tracing/core/consumer.h"
 #include "perfetto/tracing/core/trace_config.h"
-#include "perfetto/tracing/core/trace_packet.h"
 
 // TODO(fmayer): Add a test to check to what happens when ConsumerIPCClientImpl
 // gets destroyed w.r.t. the Consumer pointer. Also think to lifetime of the
@@ -71,8 +70,6 @@
     return;
   }
 
-  // Serialize the |trace_config| into a EnableTracingRequest protobuf.
-  // Keep this in sync with changes in consumer_port.proto.
   protos::EnableTracingRequest req;
   trace_config.ToProto(req.mutable_trace_config());
   ipc::Deferred<protos::EnableTracingResponse> async_response;
@@ -132,16 +129,15 @@
     PERFETTO_DLOG("ReadBuffers() failed");
     return;
   }
-  // TODO(primiano): We have to guarantee that the log buffer stays alive at
-  // least as long as these requests are on flights.
   std::vector<TracePacket> trace_packets;
-  trace_packets.reserve(response->trace_packets().size());
-  for (const std::string& bytes : response->trace_packets()) {
-    trace_packets.emplace_back();
-    trace_packets.back().AddSlice(
-        Slice(reinterpret_cast<const void*>(bytes.data()), bytes.size()));
+  for (auto& resp_slice : *response->mutable_slices()) {
+    partial_packet_.AddSlice(
+        Slice(std::unique_ptr<std::string>(resp_slice.release_data())));
+    if (resp_slice.last_slice_for_packet())
+      trace_packets.emplace_back(std::move(partial_packet_));
   }
-  consumer_->OnTraceData(std::move(trace_packets), response.has_more());
+  if (!trace_packets.empty() || !response.has_more())
+    consumer_->OnTraceData(std::move(trace_packets), response.has_more());
 }
 
 void ConsumerIPCClientImpl::FreeBuffers() {
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
index 57038ce..d94b658 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
@@ -26,6 +26,7 @@
 #include "perfetto/ipc/service_proxy.h"
 #include "perfetto/tracing/core/basic_types.h"
 #include "perfetto/tracing/core/service.h"
+#include "perfetto/tracing/core/trace_packet.h"
 #include "perfetto/tracing/ipc/consumer_ipc_client.h"
 
 #include "perfetto/ipc/consumer_port.ipc.h"
@@ -84,6 +85,13 @@
 
   bool connected_ = false;
 
+  // When a packet is too big to fit into a ReadBuffersResponse IPC, the service
+  // will chunk it into several IPCs, each containing few slices of the packet
+  // (a packet's slice is always guaranteed to be << kIPCBufferSize). When
+  // chunking happens this field accumulates the slices received until the
+  // one with |last_slice_for_packet| == true is received.
+  TracePacket partial_packet_;
+
   base::WeakPtrFactory<ConsumerIPCClientImpl> weak_ptr_factory_;
 };
 
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index ee4431e..10b36d4 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -109,7 +109,6 @@
     const protos::GetAsyncCommandResponse& cmd) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (cmd.cmd_case() == protos::GetAsyncCommandResponse::kStartDataSource) {
-    // Keep this in sync with chages in data_source_config.proto.
     const auto& req = cmd.start_data_source();
     const DataSourceInstanceID dsid = req.new_instance_id();
     DataSourceConfig cfg;
@@ -149,38 +148,24 @@
 }
 
 void ProducerIPCClientImpl::RegisterDataSource(
-    const DataSourceDescriptor& descriptor,
-    RegisterDataSourceCallback callback) {
+    const DataSourceDescriptor& descriptor) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (!connected_) {
     PERFETTO_DLOG(
         "Cannot RegisterDataSource(), not connected to tracing service");
-    return task_runner_->PostTask(std::bind(callback, 0));
   }
   protos::RegisterDataSourceRequest req;
   descriptor.ToProto(req.mutable_data_source_descriptor());
   ipc::Deferred<protos::RegisterDataSourceResponse> async_response;
-  // TODO(fmayer): add a test that destroys the IPC channel soon after this call
-  // and checks that the callback(0) is invoked.
-  // TODO(fmayer): add a test that destroys ProducerIPCClientImpl soon after
-  // this call and checks that the callback is dropped.
   async_response.Bind(
-      [callback](
-          ipc::AsyncResult<protos::RegisterDataSourceResponse> response) {
-        if (!response) {
+      [](ipc::AsyncResult<protos::RegisterDataSourceResponse> response) {
+        if (!response)
           PERFETTO_DLOG("RegisterDataSource() failed: connection reset");
-          return callback(0);
-        }
-        if (response->data_source_id() == 0) {
-          PERFETTO_DLOG("RegisterDataSource() failed: %s",
-                        response->error().c_str());
-        }
-        callback(response->data_source_id());
       });
   producer_port_.RegisterDataSource(req, std::move(async_response));
 }
 
-void ProducerIPCClientImpl::UnregisterDataSource(DataSourceID id) {
+void ProducerIPCClientImpl::UnregisterDataSource(const std::string& name) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (!connected_) {
     PERFETTO_DLOG(
@@ -188,7 +173,7 @@
     return;
   }
   protos::UnregisterDataSourceRequest req;
-  req.set_data_source_id(id);
+  req.set_data_source_name(name);
   producer_port_.UnregisterDataSource(
       req, ipc::Deferred<protos::UnregisterDataSourceResponse>());
 }
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.h b/src/tracing/ipc/producer/producer_ipc_client_impl.h
index a088d25..1f4ec68 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -60,9 +60,8 @@
   // Service::ProducerEndpoint implementation.
   // These methods are invoked by the actual Producer(s) code by clients of the
   // tracing library, which know nothing about the IPC transport.
-  void RegisterDataSource(const DataSourceDescriptor&,
-                          RegisterDataSourceCallback) override;
-  void UnregisterDataSource(DataSourceID) override;
+  void RegisterDataSource(const DataSourceDescriptor&) override;
+  void UnregisterDataSource(const std::string& name) override;
   void CommitData(const CommitDataRequest&, CommitDataCallback) override;
   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer) override;
diff --git a/src/tracing/ipc/service/consumer_ipc_service.cc b/src/tracing/ipc/service/consumer_ipc_service.cc
index e84d83b..24643d0 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.cc
+++ b/src/tracing/ipc/service/consumer_ipc_service.cc
@@ -21,8 +21,10 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ipc/basic_types.h"
 #include "perfetto/ipc/host.h"
 #include "perfetto/tracing/core/service.h"
+#include "perfetto/tracing/core/shared_memory_abi.h"
 #include "perfetto/tracing/core/slice.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
@@ -120,16 +122,49 @@
     return;
 
   auto result = ipc::AsyncResult<protos::ReadBuffersResponse>::Create();
-  result.set_has_more(has_more);
-  // TODO(primiano): Expose the slices to the Consumer rather than stitching
-  // them and wasting cpu time to hide this detail.
+
+  // A TracePacket might be too big to fit into a single IPC message (max
+  // kIPCBufferSize). However a TracePacket is made of slices and each slice
+  // is way smaller than kIPCBufferSize (a slice size is effectively bounded by
+  // the max chunk size of the SharedMemoryABI). When sending a TracePacket,
+  // if its slices don't fit within one IPC, chunk them over several contiguous
+  // IPCs using the |last_slice_for_packet| for glueing on the other side.
+  static_assert(ipc::kIPCBufferSize >= SharedMemoryABI::kMaxPageSize * 2,
+                "kIPCBufferSize too small given the max possible slice size");
+
+  auto send_ipc_reply = [this, &result](bool more) {
+    result.set_has_more(more);
+    read_buffers_response.Resolve(std::move(result));
+    result = ipc::AsyncResult<protos::ReadBuffersResponse>::Create();
+  };
+
+  size_t approx_reply_size = 0;
   for (const TracePacket& trace_packet : trace_packets) {
-    std::string* dst = result->add_trace_packets();
-    dst->reserve(trace_packet.size());
-    for (const Slice& slice : trace_packet.slices())
-      dst->append(reinterpret_cast<const char*>(slice.start), slice.size);
+    size_t num_slices_left_for_packet = trace_packet.slices().size();
+    for (const Slice& slice : trace_packet.slices()) {
+      // Check if this slice would cause the IPC to overflow its max size and,
+      // if that is the case, split the IPCs. The "16" and "64" below are
+      // over-estimations of, respectively:
+      // 16: the preamble that prefixes each slice (there are 2 x size fields
+      //     in the proto + the |last_slice_for_packet| bool).
+      // 64: the overhead of the IPC InvokeMethodReply + wire_protocol's frame.
+      // If these estimations are wrong, BufferedFrameDeserializer::Serialize()
+      // will hit a DCHECK anyways.
+      const size_t approx_slice_size = slice.size + 16;
+      if (approx_reply_size + approx_slice_size > ipc::kIPCBufferSize - 64) {
+        // If we hit this CHECK we got a single slice that is > kIPCBufferSize.
+        PERFETTO_CHECK(result->slices_size() > 0);
+        send_ipc_reply(/*has_more=*/true);
+        approx_reply_size = 0;
+      }
+      approx_reply_size += approx_slice_size;
+
+      auto* res_slice = result->add_slices();
+      res_slice->set_last_slice_for_packet(--num_slices_left_for_packet == 0);
+      res_slice->set_data(slice.start, slice.size);
+    }
   }
-  read_buffers_response.Resolve(std::move(result));
+  send_ipc_reply(has_more);
 }
 
 }  // namespace perfetto
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index 3132967..565076d 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -93,54 +93,13 @@
     return response.Reject();
   }
 
-  const std::string data_source_name = req.data_source_descriptor().name();
-  if (producer->pending_data_sources.count(data_source_name)) {
-    PERFETTO_DLOG(
-        "A RegisterDataSource() request for \"%s\" is already pending",
-        data_source_name.c_str());
-    return response.Reject();
-  }
-
-  // Deserialize IPC proto -> core DataSourceDescriptor. Keep this in sync with
-  // changes to data_source_descriptor.proto.
   DataSourceDescriptor dsd;
-  dsd.set_name(data_source_name);
-  producer->pending_data_sources[data_source_name] = std::move(response);
-  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  dsd.FromProto(req.data_source_descriptor());
+  GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(dsd);
 
-  // TODO(fmayer): add test to cover the case of IPC going away before the
-  // RegisterDataSource callback is received.
-  const ipc::ClientID ipc_client_id = ipc::Service::client_info().client_id();
-  GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(
-      dsd, [weak_this, ipc_client_id, data_source_name](DataSourceID id) {
-        if (!weak_this)
-          return;
-        weak_this->OnDataSourceRegistered(ipc_client_id, data_source_name, id);
-      });
-}
-
-// Called by the Service business logic.
-void ProducerIPCService::OnDataSourceRegistered(
-    ipc::ClientID ipc_client_id,
-    const std::string& data_source_name,
-    DataSourceID id) {
-  auto producer_it = producers_.find(ipc_client_id);
-  if (producer_it == producers_.end())
-    return;  // The producer died in the meantime.
-  RemoteProducer* producer = producer_it->second.get();
-
-  auto it = producer->pending_data_sources.find(data_source_name);
-  PERFETTO_CHECK(it != producer->pending_data_sources.end());
-
-  PERFETTO_DLOG("Data source %s registered, Client:%" PRIu64 " ID: %" PRIu64,
-                data_source_name.c_str(), ipc_client_id, id);
-
-  DeferredRegisterDataSourceResponse ipc_response = std::move(it->second);
-  producer->pending_data_sources.erase(it);
-  auto response =
-      ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create();
-  response->set_data_source_id(id);
-  ipc_response.Resolve(std::move(response));
+  // RegisterDataSource doesn't expect any meaningful response.
+  response.Resolve(
+      ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create());
 }
 
 // Called by the IPC layer.
@@ -165,7 +124,7 @@
         "InitializeConnection()");
     return response.Reject();
   }
-  producer->service_endpoint->UnregisterDataSource(req.data_source_id());
+  producer->service_endpoint->UnregisterDataSource(req.data_source_name());
 
   // UnregisterDataSource doesn't expect any meaningful response.
   response.Resolve(
diff --git a/src/tracing/ipc/service/producer_ipc_service.h b/src/tracing/ipc/service/producer_ipc_service.h
index 128a30b..289c036 100644
--- a/src/tracing/ipc/service/producer_ipc_service.h
+++ b/src/tracing/ipc/service/producer_ipc_service.h
@@ -75,10 +75,6 @@
     void OnTracingStart() override;
     void OnTracingStop() override;
 
-    // RegisterDataSource requests that haven't been replied yet.
-    std::map<std::string, DeferredRegisterDataSourceResponse>
-        pending_data_sources;
-
     // The interface obtained from the core service business logic through
     // Service::ConnectProducer(this). This allows to invoke methods for a
     // specific Producer on the Service business logic.
@@ -97,10 +93,6 @@
   // the current IPC request.
   RemoteProducer* GetProducerForCurrentRequest();
 
-  // Called back by the |core_service_| business logic, soon after a call to
-  // RegisterDataSource().
-  void OnDataSourceRegistered(ipc::ClientID, const std::string&, DataSourceID);
-
   Service* const core_service_;
 
   // Maps IPC clients to ProducerEndpoint instances registered on the
diff --git a/src/tracing/test/fake_packet.cc b/src/tracing/test/fake_packet.cc
index fd5ca05..2a254da 100644
--- a/src/tracing/test/fake_packet.cc
+++ b/src/tracing/test/fake_packet.cc
@@ -103,7 +103,7 @@
             << packet.payload() << "\"}";
 }
 
-FakeChunk::FakeChunk(TraceBuffez* t, ProducerID p, WriterID w, ChunkID c)
+FakeChunk::FakeChunk(TraceBuffer* t, ProducerID p, WriterID w, ChunkID c)
     : trace_buffer_{t}, producer_id{p}, writer_id{w}, chunk_id{c} {}
 
 FakeChunk& FakeChunk::AddPacket(size_t size, char seed, uint8_t packet_flag) {
@@ -141,7 +141,7 @@
   trace_buffer_->CopyChunkUntrusted(producer_id, uid, writer_id, chunk_id,
                                     num_packets, flags, data.data(),
                                     data.size());
-  return data.size() + TraceBuffez::InlineChunkHeaderSize;
+  return data.size() + TraceBuffer::InlineChunkHeaderSize;
 }
 
 }  // namespace perfetto
diff --git a/src/tracing/test/fake_packet.h b/src/tracing/test/fake_packet.h
index daab5e8..b3e56d8 100644
--- a/src/tracing/test/fake_packet.h
+++ b/src/tracing/test/fake_packet.h
@@ -28,7 +28,7 @@
 
 namespace perfetto {
 
-class TraceBuffez;
+class TraceBuffer;
 
 class FakePacketFragment {
  public:
@@ -51,7 +51,7 @@
 
 class FakeChunk {
  public:
-  FakeChunk(TraceBuffez* t, ProducerID p, WriterID w, ChunkID c);
+  FakeChunk(TraceBuffer* t, ProducerID p, WriterID w, ChunkID c);
 
   // Appends a packet of exactly |size| bytes (including the varint header
   // that states the size of the packet itself. The payload of the packet is
@@ -70,7 +70,7 @@
   size_t CopyIntoTraceBuffer();
 
  private:
-  TraceBuffez* trace_buffer_;
+  TraceBuffer* trace_buffer_;
   ProducerID producer_id;
   WriterID writer_id;
   ChunkID chunk_id;
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index 426f469..0f8dba6 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -93,7 +93,7 @@
 
     // Create and connect a Producer.
     producer_endpoint_ = ProducerIPCClient::Connect(
-        kProducerSockName, &producer_, "com.google.perfetto.mock_producer",
+        kProducerSockName, &producer_, "perfetto.mock_producer",
         task_runner_.get());
     auto on_producer_connect =
         task_runner_->CreateCheckpoint("on_producer_connect");
@@ -103,14 +103,7 @@
     // Register a data source.
     DataSourceDescriptor ds_desc;
     ds_desc.set_name("perfetto.test");
-    auto on_data_source_registered =
-        task_runner_->CreateCheckpoint("on_data_source_registered");
-    producer_endpoint_->RegisterDataSource(
-        ds_desc, [on_data_source_registered](DataSourceID dsid) {
-          PERFETTO_DLOG("Registered data source with ID: %" PRIu64, dsid);
-          on_data_source_registered();
-        });
-    task_runner_->RunUntilCheckpoint("on_data_source_registered");
+    producer_endpoint_->RegisterDataSource(ds_desc);
 
     // Create and connect a Consumer.
     consumer_endpoint_ = ConsumerIPCClient::Connect(
@@ -218,6 +211,12 @@
           Invoke([&num_pack_rx, all_packets_rx, &trace_config,
                   &saw_clock_snapshot, &saw_trace_config](
                      std::vector<TracePacket>* packets, bool has_more) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
+            const int kExpectedMinNumberOfClocks = 1;
+#else
+            const int kExpectedMinNumberOfClocks = 6;
+#endif
+
             for (auto& packet : *packets) {
               ASSERT_TRUE(packet.Decode());
               if (packet->has_for_testing()) {
@@ -225,7 +224,8 @@
                 sprintf(buf, "evt_%zu", num_pack_rx++);
                 EXPECT_EQ(std::string(buf), packet->for_testing().str());
               } else if (packet->has_clock_snapshot()) {
-                EXPECT_GE(packet->clock_snapshot().clocks_size(), 6);
+                EXPECT_GE(packet->clock_snapshot().clocks_size(),
+                          kExpectedMinNumberOfClocks);
                 saw_clock_snapshot = true;
               } else if (packet->has_trace_config()) {
                 protos::TraceConfig config_proto;
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 7e27608..de7c72c 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -25,9 +25,9 @@
 source_set("end_to_end_integrationtests") {
   testonly = true
   deps = [
-    ":fake_consumer",
     ":task_runner_thread",
     ":task_runner_thread_delegates",
+    ":test_helper",
     "../gn:default_deps",
     "../gn:gtest_deps",
     "../protos/perfetto/trace:lite",
@@ -52,9 +52,9 @@
     ]
     testonly = true
     deps = [
-      ":fake_consumer",
       ":task_runner_thread",
       ":task_runner_thread_delegates",
+      ":test_helper",
       "../gn:default_deps",
       "../src/base:test_support",
       "../src/protozero",
@@ -65,22 +65,6 @@
   }
 }
 
-source_set("fake_consumer") {
-  testonly = true
-  deps = [
-    "../gn:default_deps",
-    "../gn:gtest_deps",
-    "../protos/perfetto/trace:lite",
-    "../protos/perfetto/trace:zero",
-    "../src/base:base",
-    "../src/base:test_support",
-  ]
-  sources = [
-    "fake_consumer.cc",
-    "fake_consumer.h",
-  ]
-}
-
 source_set("task_runner_thread") {
   testonly = true
   deps = [
@@ -109,13 +93,31 @@
   ]
 }
 
+source_set("test_helper") {
+  testonly = true
+  deps = [
+    ":task_runner_thread",
+    ":task_runner_thread_delegates",
+    "../gn:default_deps",
+    "../protos/perfetto/trace:lite",
+    "../src/base:test_support",
+  ]
+  sources = [
+    "test_helper.cc",
+    "test_helper.h",
+  ]
+  if (start_daemons_for_testing) {
+    cflags = [ "-DPERFETTO_START_DAEMONS_FOR_TESTING" ]
+  }
+}
+
 if (!build_with_chromium) {
   source_set("end_to_end_benchmarks") {
     testonly = true
     deps = [
-      ":fake_consumer",
       ":task_runner_thread",
       ":task_runner_thread_delegates",
+      ":test_helper",
       "../../gn:default_deps",
       "../gn:gtest_deps",
       "../protos/perfetto/trace:lite",
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 3923c02..485d4f6 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -27,6 +27,7 @@
       "atrace.cfg",
       "ftrace.cfg",
       "long_trace.cfg",
+      "processes.cfg",
     ]
 
     outputs = [
diff --git a/test/configs/atrace.cfg b/test/configs/atrace.cfg
index dde7f38..1362a20 100644
--- a/test/configs/atrace.cfg
+++ b/test/configs/atrace.cfg
@@ -6,10 +6,10 @@
 # TODO(hjd): Add some atrace config here.
 data_sources {
   config {
-    name: "com.google.perfetto.ftrace"
+    name: "linux.ftrace"
     target_buffer: 0
     ftrace_config {
-      ftrace_events: "sched_switch"
+      atrace_categories: "input"
       buffer_size_kb: 168  # 4 (page size) * 42
       drain_period_ms: 42
     }
diff --git a/test/configs/ftrace.cfg b/test/configs/ftrace.cfg
index 2bb2810..4fbc0a5 100644
--- a/test/configs/ftrace.cfg
+++ b/test/configs/ftrace.cfg
@@ -5,7 +5,7 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.ftrace"
+    name: "linux.ftrace"
     target_buffer: 0
     ftrace_config {
       buffer_size_kb: 40 # 4 (page size) * 10
@@ -245,20 +245,30 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.process_stats"
+    name: "linux.process_stats"
     target_buffer: 0
   }
 }
 
 data_sources {
   config {
-    name: "com.google.perfetto.inode_file_map"
+    name: "linux.inode_file_map"
     target_buffer: 0
+    inode_file_config {
+      scan_delay_ms: 1000
+      scan_interval_ms: 1000
+      scan_batch_size: 100000
+      mount_point_mapping: {
+        mountpoint: "/data"
+        scan_roots: "/data/app"
+        scan_roots: "/data/dalvik-cache"
+      }
+    }
   }
 }
 
 producers {
-  producer_name: "com.google.perfetto.traced_probes"
+  producer_name: "perfetto.traced_probes"
   shm_size_kb: 4096
   page_size_kb: 4
 }
diff --git a/test/configs/long_trace.cfg b/test/configs/long_trace.cfg
index ee62800..e81ecab 100644
--- a/test/configs/long_trace.cfg
+++ b/test/configs/long_trace.cfg
@@ -10,7 +10,7 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.ftrace"
+    name: "linux.ftrace"
     target_buffer: 1
     ftrace_config {
       buffer_size_kb: 40 # 4 (page size) * 10
@@ -141,20 +141,20 @@
 
 data_sources {
   config {
-    name: "com.google.perfetto.process_stats"
+    name: "linux.process_stats"
     target_buffer: 0
   }
 }
 
 data_sources {
   config {
-    name: "com.google.perfetto.inode_file_map"
+    name: "linux.inode_file_map"
     target_buffer: 0
   }
 }
 
 producers {
-  producer_name: "com.google.perfetto.traced_probes"
+  producer_name: "perfetto.traced_probes"
   shm_size_kb: 4096
   page_size_kb: 4
 }
diff --git a/test/configs/processes.cfg b/test/configs/processes.cfg
new file mode 100644
index 0000000..f8c24b8
--- /dev/null
+++ b/test/configs/processes.cfg
@@ -0,0 +1,57 @@
+buffers {
+  size_kb: 100024
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "com.google.perfetto.ftrace"
+    target_buffer: 0
+    ftrace_config {
+      buffer_size_kb: 40 # 4 (page size) * 10
+      drain_period_ms: 200
+      ftrace_events: "sched_process_exec"
+      ftrace_events: "sched_process_exit"
+      ftrace_events: "sched_process_fork"
+      ftrace_events: "sched_process_free"
+      ftrace_events: "sched_process_hang"
+      ftrace_events: "sched_process_wait"
+      ftrace_events: "sched_wakeup_new"
+      ftrace_events: "sched_wakeup"
+      ftrace_events: "sched_waking"
+      ftrace_events: "smbus_read"
+      ftrace_events: "smbus_reply"
+      ftrace_events: "smbus_result"
+      ftrace_events: "smbus_write"
+      ftrace_events: "softirq_entry"
+      ftrace_events: "softirq_exit"
+      ftrace_events: "softirq_raise"
+      ftrace_events: "suspend_resume"
+      ftrace_events: "sync_pt"
+      ftrace_events: "sync_timeline"
+      ftrace_events: "sync_wait"
+      ftrace_events: "task_newtask"
+      ftrace_events: "task_rename"
+      ftrace_events: "tracing_mark_write"
+      ftrace_events: "workqueue_activate_work"
+      ftrace_events: "workqueue_execute_end"
+      ftrace_events: "workqueue_execute_start"
+      ftrace_events: "workqueue_queue_work"
+    }
+  }
+}
+
+data_sources {
+  config {
+    name: "com.google.perfetto.process_stats"
+    target_buffer: 0
+  }
+}
+
+producers {
+  producer_name: "com.google.perfetto.traced_probes"
+  shm_size_kb: 4096
+  page_size_kb: 4
+}
+
+duration_ms: 10000
diff --git a/test/cts/end_to_end_integrationtest_cts.cc b/test/cts/end_to_end_integrationtest_cts.cc
index d6531ec..426458b 100644
--- a/test/cts/end_to_end_integrationtest_cts.cc
+++ b/test/cts/end_to_end_integrationtest_cts.cc
@@ -22,7 +22,8 @@
 #include "perfetto/trace/trace_packet.pbzero.h"
 #include "perfetto/traced/traced.h"
 #include "perfetto/tracing/core/trace_packet.h"
-#include "test/fake_consumer.h"
+#include "src/base/test/test_task_runner.h"
+#include "test/test_helper.h"
 
 namespace perfetto {
 
@@ -31,65 +32,45 @@
   void TestMockProducer(const std::string& producer_name) {
     base::TestTaskRunner task_runner;
 
-    // Setup the trace config.
+    TestHelper helper(&task_runner);
+    helper.ConnectConsumer();
+
     TraceConfig trace_config;
-    trace_config.add_buffers()->set_size_kb(4096 * 10);
+    trace_config.add_buffers()->set_size_kb(1024);
     trace_config.set_duration_ms(200);
 
     auto* ds_config = trace_config.add_data_sources()->mutable_config();
     ds_config->set_name(producer_name);
     ds_config->set_target_buffer(0);
 
-    // The parameters for the producer.
     static constexpr uint32_t kRandomSeed = 42;
     static constexpr uint32_t kEventCount = 10;
     static constexpr uint32_t kMessageSizeBytes = 1024;
-
-    // Setup the test to use a random number generator.
     ds_config->mutable_for_testing()->set_seed(kRandomSeed);
     ds_config->mutable_for_testing()->set_message_count(kEventCount);
     ds_config->mutable_for_testing()->set_message_size(kMessageSizeBytes);
 
-    // Create the random generator with the same seed.
-    std::minstd_rand0 rnd_engine(kRandomSeed);
+    auto producer_enabled = task_runner.CreateCheckpoint("producer.enabled");
+    task_runner.PostTask(producer_enabled);
+    helper.StartTracing(trace_config);
 
-    // Setip the function.
-    uint64_t total = 0;
+    size_t packets_seen = 0;
+    std::minstd_rand0 rnd_engine(kRandomSeed);
+    auto on_consumer_data = [&packets_seen, &rnd_engine](
+                                const TracePacket::DecodedTracePacket& packet) {
+      ASSERT_TRUE(packet.has_for_testing());
+      ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
+      packets_seen++;
+    };
     auto on_readback_complete =
         task_runner.CreateCheckpoint("readback.complete");
-    auto function = [&total, &on_readback_complete, &rnd_engine](
-                        std::vector<TracePacket> packets, bool has_more) {
-      for (auto& packet : packets) {
-        ASSERT_TRUE(packet.Decode());
-        ASSERT_TRUE(packet->has_for_testing() || packet->has_clock_snapshot() ||
-                    packet->has_trace_config());
-        if (packet->has_clock_snapshot() || packet->has_trace_config()) {
-          continue;
-        }
-        ASSERT_EQ(protos::TracePacket::kTrustedUid,
-                  packet->optional_trusted_uid_case());
-        ASSERT_EQ(packet->for_testing().seq_value(), rnd_engine());
-      }
-      total += packets.size();
-
-      if (!has_more) {
-        // Extra packets for the clock snapshot and trace config.
-        ASSERT_EQ(total, kEventCount + 2);
-        on_readback_complete();
-      }
-    };
-
-    // Finally, make the consumer connect to the service.
-    auto on_connect = task_runner.CreateCheckpoint("consumer.connected");
-    FakeConsumer consumer(trace_config, std::move(on_connect),
-                          std::move(function), &task_runner);
-    consumer.Connect(PERFETTO_CONSUMER_SOCK_NAME);
-    task_runner.RunUntilCheckpoint("consumer.connected");
-
-    consumer.EnableTracing();
-    task_runner.PostDelayedTask([&consumer]() { consumer.ReadTraceData(); },
-                                1000);
+    task_runner.PostDelayedTask(
+        [&on_consumer_data, &on_readback_complete, &helper]() {
+          helper.ReadData(on_consumer_data, on_readback_complete);
+        },
+        1000);
     task_runner.RunUntilCheckpoint("readback.complete");
+    ASSERT_EQ(packets_seen, kEventCount);
   }
 };
 
diff --git a/test/end_to_end_benchmark.cc b/test/end_to_end_benchmark.cc
index 57f9cae..429711a 100644
--- a/test/end_to_end_benchmark.cc
+++ b/test/end_to_end_benchmark.cc
@@ -23,42 +23,26 @@
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
 #include "src/base/test/test_task_runner.h"
-#include "test/fake_consumer.h"
 #include "test/task_runner_thread.h"
 #include "test/task_runner_thread_delegates.h"
+#include "test/test_helper.h"
 
 namespace perfetto {
 
-// If we're building on Android and starting the daemons ourselves,
-// create the sockets in a world-writable location.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
-#define TEST_CONSUMER_SOCK_NAME "/data/local/tmp/traced_consumer"
-#else
-#define TEST_PRODUCER_SOCK_NAME PERFETTO_PRODUCER_SOCK_NAME
-#define TEST_CONSUMER_SOCK_NAME PERFETTO_CONSUMER_SOCK_NAME
-#endif
+namespace {
 
-static void BM_EndToEnd(benchmark::State& state) {
+bool IsBenchmarkFunctionalOnly() {
+  return getenv("BENCHMARK_FUNCTIONAL_TEST_ONLY") != nullptr;
+}
+
+void BenchmarkCommon(benchmark::State& state) {
   base::TestTaskRunner task_runner;
 
-#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread service_thread("perfetto.svc");
-  service_thread.Start(std::unique_ptr<ServiceDelegate>(
-      new ServiceDelegate(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME)));
-#endif
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
 
-  TaskRunnerThread producer_thread("perfetto.prd");
-  auto on_producer_enabled = task_runner.CreateCheckpoint("producer.enabled");
-  auto posted_on_producer_enabled = [&task_runner, &on_producer_enabled] {
-    task_runner.PostTask(on_producer_enabled);
-  };
-  std::unique_ptr<FakeProducerDelegate> producer_delegate(
-      new FakeProducerDelegate(TEST_PRODUCER_SOCK_NAME,
-                               posted_on_producer_enabled));
-  FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
-  producer_thread.Start(std::move(producer_delegate));
+  FakeProducer* producer = helper.ConnectFakeProducer();
+  helper.ConnectConsumer();
 
   // Setup the TraceConfig for the consumer.
   TraceConfig trace_config;
@@ -71,85 +55,101 @@
 
   // The parameters for the producer.
   static constexpr uint32_t kRandomSeed = 42;
-  uint32_t message_count = state.range(0);
-  uint32_t message_size = state.range(1);
+  size_t message_count = state.range(0);
+  size_t message_bytes = state.range(1);
+  size_t mb_per_s = state.range(2);
+
+  size_t messages_per_s = mb_per_s * 1024 * 1024 / message_bytes;
+  size_t time_for_messages_ms =
+      10000 + (messages_per_s == 0 ? 0 : message_count * 1000 / messages_per_s);
 
   // Setup the test to use a random number generator.
   ds_config->mutable_for_testing()->set_seed(kRandomSeed);
   ds_config->mutable_for_testing()->set_message_count(message_count);
-  ds_config->mutable_for_testing()->set_message_size(message_size);
+  ds_config->mutable_for_testing()->set_message_size(message_bytes);
+  ds_config->mutable_for_testing()->set_max_messages_per_second(messages_per_s);
+
+  helper.StartTracing(trace_config);
 
   bool is_first_packet = true;
-  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
   std::minstd_rand0 rnd_engine(kRandomSeed);
-  auto on_consumer_data = [&is_first_packet, &on_readback_complete,
-                           &rnd_engine](std::vector<TracePacket> packets,
-                                        bool has_more) {
-    for (auto& packet : packets) {
-      ASSERT_TRUE(packet.Decode());
-      ASSERT_TRUE(packet->has_for_testing() || packet->has_clock_snapshot());
-      if (packet->has_clock_snapshot()) {
-        continue;
-      }
-      ASSERT_EQ(protos::TracePacket::kTrustedUid,
-                packet->optional_trusted_uid_case());
-      if (is_first_packet) {
-        rnd_engine = std::minstd_rand0(packet->for_testing().seq_value());
-        is_first_packet = false;
-      } else {
-        ASSERT_EQ(packet->for_testing().seq_value(), rnd_engine());
-      }
-    }
-
-    if (!has_more) {
-      is_first_packet = true;
-      on_readback_complete();
+  auto on_consumer_data = [&is_first_packet, &rnd_engine](
+                              const TracePacket::DecodedTracePacket& packet) {
+    ASSERT_TRUE(packet.has_for_testing());
+    if (is_first_packet) {
+      rnd_engine = std::minstd_rand0(packet.for_testing().seq_value());
+      is_first_packet = false;
+    } else {
+      ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
     }
   };
 
-  // Finally, make the consumer connect to the service.
-  auto on_connect = task_runner.CreateCheckpoint("consumer.connected");
-  FakeConsumer consumer(trace_config, std::move(on_connect),
-                        std::move(on_consumer_data), &task_runner);
-  consumer.Connect(TEST_CONSUMER_SOCK_NAME);
-  task_runner.RunUntilCheckpoint("consumer.connected");
-
-  consumer.EnableTracing();
-  task_runner.RunUntilCheckpoint("producer.enabled");
-
   uint64_t wall_start_ns = base::GetWallTimeNs().count();
-  uint64_t thread_start_ns = service_thread.GetThreadCPUTimeNs();
+  uint64_t service_start_ns = helper.service_thread()->GetThreadCPUTimeNs();
+  uint64_t producer_start_ns = helper.producer_thread()->GetThreadCPUTimeNs();
   uint64_t iterations = 0;
   for (auto _ : state) {
     auto cname = "produced.and.committed." + std::to_string(iterations++);
     auto on_produced_and_committed = task_runner.CreateCheckpoint(cname);
-    auto posted_on_produced_and_committed = [&task_runner,
-                                             &on_produced_and_committed] {
-      task_runner.PostTask(on_produced_and_committed);
-    };
-    FakeProducer* producer = producer_delegate_cached->producer();
-    producer->ProduceEventBatch(posted_on_produced_and_committed);
-    task_runner.RunUntilCheckpoint(cname);
+    producer->ProduceEventBatch(helper.WrapTask(on_produced_and_committed));
+    task_runner.RunUntilCheckpoint(cname, time_for_messages_ms);
   }
-  uint64_t thread_ns = service_thread.GetThreadCPUTimeNs() - thread_start_ns;
+  uint64_t service_ns =
+      helper.service_thread()->GetThreadCPUTimeNs() - service_start_ns;
+  uint64_t producer_ns =
+      helper.producer_thread()->GetThreadCPUTimeNs() - producer_start_ns;
   uint64_t wall_ns = base::GetWallTimeNs().count() - wall_start_ns;
 
-  state.counters["Ser CPU"] = benchmark::Counter(100.0 * thread_ns / wall_ns);
+  state.counters["Pro CPU"] = benchmark::Counter(100.0 * producer_ns / wall_ns);
+  state.counters["Ser CPU"] = benchmark::Counter(100.0 * service_ns / wall_ns);
   state.counters["Ser ns/m"] =
-      benchmark::Counter(1.0 * thread_ns / message_count);
+      benchmark::Counter(1.0 * service_ns / message_count);
 
   // Read back the buffer just to check correctness.
-  consumer.ReadTraceData();
+  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
+  helper.ReadData(on_consumer_data, on_readback_complete);
   task_runner.RunUntilCheckpoint("readback.complete");
-  state.SetBytesProcessed(int64_t(state.iterations()) * message_size *
-                          message_count);
-
-  consumer.Disconnect();
+  state.SetBytesProcessed(iterations * message_bytes * message_count);
 }
 
-BENCHMARK(BM_EndToEnd)
+void SaturateCpuArgs(benchmark::internal::Benchmark* b) {
+  int min_message_count = 16;
+  int max_message_count = IsBenchmarkFunctionalOnly() ? 1024 : 1024 * 1024;
+  int min_payload = 8;
+  int max_payload = IsBenchmarkFunctionalOnly() ? 256 : 2048;
+  for (int count = min_message_count; count <= max_message_count; count *= 2) {
+    for (int bytes = min_payload; bytes <= max_payload; bytes *= 2) {
+      b->Args({count, bytes, 0 /* speed */});
+    }
+  }
+}
+
+void ConstantRateArgs(benchmark::internal::Benchmark* b) {
+  int message_count = IsBenchmarkFunctionalOnly() ? 2 * 1024 : 128 * 1024;
+  int min_speed = IsBenchmarkFunctionalOnly() ? 64 : 8;
+  int max_speed = IsBenchmarkFunctionalOnly() ? 128 : 128;
+  for (int speed = min_speed; speed <= max_speed; speed *= 2) {
+    b->Args({message_count, 128, speed});
+    b->Args({message_count, 256, speed});
+  }
+}
+}  // namespace
+
+static void BM_EndToEnd_SaturateCpu(benchmark::State& state) {
+  BenchmarkCommon(state);
+}
+
+BENCHMARK(BM_EndToEnd_SaturateCpu)
     ->Unit(benchmark::kMicrosecond)
     ->UseRealTime()
-    ->RangeMultiplier(2)
-    ->Ranges({{16, 1024 * 1024}, {8, 2048}});
+    ->Apply(SaturateCpuArgs);
+
+static void BM_EndToEnd_ConstantRate(benchmark::State& state) {
+  BenchmarkCommon(state);
 }
+
+BENCHMARK(BM_EndToEnd_ConstantRate)
+    ->Unit(benchmark::kMicrosecond)
+    ->UseRealTime()
+    ->Apply(ConstantRateArgs);
+}  // namespace perfetto
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index daff489..14f8c3b 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -27,36 +27,22 @@
 #include "perfetto/trace/trace_packet.pb.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
 #include "perfetto/traced/traced.h"
-#include "perfetto/tracing/core/consumer.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
-#include "perfetto/tracing/ipc/consumer_ipc_client.h"
 #include "src/base/test/test_task_runner.h"
-#include "test/fake_consumer.h"
 #include "test/task_runner_thread.h"
 #include "test/task_runner_thread_delegates.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include "perfetto/base/android_task_runner.h"
-#endif
+#include "test/test_helper.h"
 
 namespace perfetto {
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-using PlatformTaskRunner = base::AndroidTaskRunner;
-#else
-using PlatformTaskRunner = base::UnixTaskRunner;
-#endif
-
 // If we're building on Android and starting the daemons ourselves,
 // create the sockets in a world-writable location.
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
     PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
 #define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
-#define TEST_CONSUMER_SOCK_NAME "/data/local/tmp/traced_consumer"
 #else
 #define TEST_PRODUCER_SOCK_NAME PERFETTO_PRODUCER_SOCK_NAME
-#define TEST_CONSUMER_SOCK_NAME PERFETTO_CONSUMER_SOCK_NAME
 #endif
 
 // TODO(b/73453011): reenable this on more platforms (including standalone
@@ -69,161 +55,141 @@
 TEST(PerfettoTest, MAYBE_TestFtraceProducer) {
   base::TestTaskRunner task_runner;
 
-#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread service_thread("perfetto.svc");
-  service_thread.Start(std::unique_ptr<ServiceDelegate>(
-      new ServiceDelegate(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME)));
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
 
+#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
   TaskRunnerThread producer_thread("perfetto.prd");
   producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
       new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
 #endif
 
-  // Setip the TraceConfig for the consumer.
+  helper.ConnectConsumer();
+
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(1024);
   trace_config.set_duration_ms(3000);
 
-  // Create the buffer for ftrace.
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
-  ds_config->set_name("com.google.perfetto.ftrace");
+  ds_config->set_name("linux.ftrace");
   ds_config->set_target_buffer(0);
 
-  // Setup the config for ftrace.
   auto* ftrace_config = ds_config->mutable_ftrace_config();
   *ftrace_config->add_ftrace_events() = "sched_switch";
   *ftrace_config->add_ftrace_events() = "bar";
 
-  // Create the function to handle packets as they come in.
-  uint64_t total = 0;
+  auto producer_enabled = task_runner.CreateCheckpoint("producer.enabled");
+  task_runner.PostDelayedTask(producer_enabled, 100);
+  helper.StartTracing(trace_config);
+
+  size_t packets_seen = 0;
+  auto on_consumer_data =
+      [&packets_seen](const TracePacket::DecodedTracePacket& packet) {
+        for (int ev = 0; ev < packet.ftrace_events().event_size(); ev++) {
+          ASSERT_TRUE(packet.ftrace_events().event(ev).has_sched_switch());
+        }
+        packets_seen++;
+      };
   auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
-  auto on_consumer_data = [&total, &on_readback_complete](
-                              std::vector<TracePacket> packets, bool has_more) {
-    for (auto& packet : packets) {
-      ASSERT_TRUE(packet.Decode());
-      ASSERT_TRUE(packet->has_ftrace_events() || packet->has_clock_snapshot() ||
-                  packet->has_trace_config());
-      if (packet->has_clock_snapshot() || packet->has_trace_config())
-        continue;
-      for (int ev = 0; ev < packet->ftrace_events().event_size(); ev++) {
-        ASSERT_TRUE(packet->ftrace_events().event(ev).has_sched_switch());
-      }
-    }
-    total += packets.size();
-
-    if (!has_more) {
-      ASSERT_GT(total, 0u);
-      on_readback_complete();
-    }
-  };
-
-  auto on_connect = task_runner.CreateCheckpoint("consumer.connected");
-  FakeConsumer consumer(trace_config, std::move(on_connect),
-                        std::move(on_consumer_data), &task_runner);
-
-  consumer.Connect(TEST_CONSUMER_SOCK_NAME);
-  task_runner.RunUntilCheckpoint("consumer.connected");
-
-  // Traced probes should flush data as it produces it.
-  consumer.EnableTracing();
-  task_runner.PostDelayedTask([&consumer] { consumer.ReadTraceData(); }, 3000);
-
-  task_runner.RunUntilCheckpoint("readback.complete", 10000);
-
-  consumer.Disconnect();
+  task_runner.PostDelayedTask(
+      [&helper, &on_consumer_data, &on_readback_complete] {
+        helper.ReadData(on_consumer_data, on_readback_complete);
+      },
+      3000);
+  task_runner.RunUntilCheckpoint("readback.complete");
+  ASSERT_GT(packets_seen, 0u);
 }
 
 TEST(PerfettoTest, TestFakeProducer) {
   base::TestTaskRunner task_runner;
 
-#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
-  TaskRunnerThread service_thread("perfetto.svc");
-  service_thread.Start(std::unique_ptr<ServiceDelegate>(
-      new ServiceDelegate(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME)));
-#endif
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
 
-  auto on_producer_enabled = task_runner.CreateCheckpoint("producer.enabled");
-  auto posted_on_producer_enabled = [&task_runner, &on_producer_enabled] {
-    task_runner.PostTask(on_producer_enabled);
-  };
-  TaskRunnerThread producer_thread("perfetto.prd");
-  std::unique_ptr<FakeProducerDelegate> producer_delegate(
-      new FakeProducerDelegate(TEST_PRODUCER_SOCK_NAME,
-                               posted_on_producer_enabled));
-  FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
-  producer_thread.Start(std::move(producer_delegate));
+  FakeProducer* producer = helper.ConnectFakeProducer();
+  helper.ConnectConsumer();
 
-  // Setup the TraceConfig for the consumer.
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(1024);
   trace_config.set_duration_ms(200);
 
-  // Create the buffer for ftrace.
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.perfetto.FakeProducer");
   ds_config->set_target_buffer(0);
 
-  // The parameters for the producer.
+  static constexpr size_t kNumPackets = 10;
   static constexpr uint32_t kRandomSeed = 42;
-  static constexpr uint32_t kEventCount = 10;
-  static constexpr uint32_t kMessageSizeBytes = 1024;
-
-  // Setup the test to use a random number generator.
+  static constexpr uint32_t kMsgSize = 1024;
   ds_config->mutable_for_testing()->set_seed(kRandomSeed);
-  ds_config->mutable_for_testing()->set_message_count(kEventCount);
-  ds_config->mutable_for_testing()->set_message_size(kMessageSizeBytes);
+  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
+  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
 
-  // Create the random generator with the same seed.
-  std::minstd_rand0 random(kRandomSeed);
+  helper.StartTracing(trace_config);
 
-  // Create the function to handle packets as they come in.
-  uint64_t total = 0;
-  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
-  auto on_consumer_data = [&total, &on_readback_complete, &random](
-                              std::vector<TracePacket> packets, bool has_more) {
-    for (auto& packet : packets) {
-      ASSERT_TRUE(packet.Decode());
-      if (packet->has_clock_snapshot() || packet->has_trace_config())
-        continue;
-      ASSERT_TRUE(packet->has_for_testing());
-      ASSERT_EQ(protos::TracePacket::kTrustedUid,
-                packet->optional_trusted_uid_case());
-      ASSERT_EQ(packet->for_testing().seq_value(), random());
-    }
-    total += packets.size();
-
-    if (!has_more) {
-      // One extra packet for the clock snapshot and another for the trace
-      // config.
-      ASSERT_EQ(total, kEventCount + 2);
-      on_readback_complete();
-    }
-  };
-
-  auto on_connect = task_runner.CreateCheckpoint("consumer.connected");
-  FakeConsumer consumer(trace_config, std::move(on_connect),
-                        std::move(on_consumer_data), &task_runner);
-
-  consumer.Connect(TEST_CONSUMER_SOCK_NAME);
-  task_runner.RunUntilCheckpoint("consumer.connected");
-
-  consumer.EnableTracing();
-  task_runner.RunUntilCheckpoint("producer.enabled");
-
-  auto on_produced_and_committed =
-      task_runner.CreateCheckpoint("produced.and.committed");
-  auto posted_on_produced_and_committed = [&task_runner,
-                                           &on_produced_and_committed] {
-    task_runner.PostTask(on_produced_and_committed);
-  };
-  FakeProducer* producer = producer_delegate_cached->producer();
-  producer->ProduceEventBatch(posted_on_produced_and_committed);
+  producer->ProduceEventBatch(
+      helper.WrapTask(task_runner.CreateCheckpoint("produced.and.committed")));
   task_runner.RunUntilCheckpoint("produced.and.committed");
 
-  consumer.ReadTraceData();
+  size_t packets_seen = 0;
+  std::minstd_rand0 rnd_engine(kRandomSeed);
+  auto on_consumer_data = [&packets_seen, &rnd_engine](
+                              const TracePacket::DecodedTracePacket& packet) {
+    ASSERT_TRUE(packet.has_for_testing());
+    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
+    packets_seen++;
+  };
+  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
+  helper.ReadData(on_consumer_data, on_readback_complete);
   task_runner.RunUntilCheckpoint("readback.complete");
+  ASSERT_EQ(packets_seen, kNumPackets);
+}
 
-  consumer.Disconnect();
+TEST(PerfettoTest, VeryLargePackets) {
+  base::TestTaskRunner task_runner;
+
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
+
+  FakeProducer* producer = helper.ConnectFakeProducer();
+  helper.ConnectConsumer();
+
+  // Setup the TraceConfig for the consumer.
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(4096 * 10);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.perfetto.FakeProducer");
+  ds_config->set_target_buffer(0);
+
+  static constexpr size_t kNumPackets = 5;
+  static constexpr uint32_t kRandomSeed = 42;
+  static constexpr uint32_t kMsgSize = 1024 * 1024 - 42;
+  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
+  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
+  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
+
+  helper.StartTracing(trace_config);
+
+  producer->ProduceEventBatch(
+      helper.WrapTask(task_runner.CreateCheckpoint("produced.and.committed")));
+  task_runner.RunUntilCheckpoint("produced.and.committed");
+
+  size_t packets_seen = 0;
+  std::minstd_rand0 rnd_engine(kRandomSeed);
+  auto on_consumer_data = [&packets_seen, &rnd_engine](
+                              const TracePacket::DecodedTracePacket& packet) {
+    ASSERT_TRUE(packet.has_for_testing());
+    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
+    size_t msg_size = packet.for_testing().str().size();
+    ASSERT_EQ(kMsgSize, msg_size);
+    for (size_t i = 0; i < msg_size; i++)
+      ASSERT_EQ(i < msg_size - 1 ? '.' : 0, packet.for_testing().str()[i]);
+    packets_seen++;
+  };
+  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
+  helper.ReadData(on_consumer_data, on_readback_complete);
+  task_runner.RunUntilCheckpoint("readback.complete");
+  ASSERT_EQ(packets_seen, kNumPackets);
 }
 
 }  // namespace perfetto
diff --git a/test/end_to_end_shared_memory_fuzzer.cc b/test/end_to_end_shared_memory_fuzzer.cc
index 37689f7..521fe86 100644
--- a/test/end_to_end_shared_memory_fuzzer.cc
+++ b/test/end_to_end_shared_memory_fuzzer.cc
@@ -25,6 +25,7 @@
 #include "perfetto/trace/test_event.pbzero.h"
 #include "perfetto/trace/trace_packet.pb.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
+#include "perfetto/traced/traced.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "perfetto/tracing/core/producer.h"
@@ -32,15 +33,21 @@
 #include "perfetto/tracing/ipc/producer_ipc_client.h"
 #include "perfetto/tracing/ipc/service_ipc_host.h"
 #include "src/base/test/test_task_runner.h"
-#include "test/fake_consumer.h"
 #include "test/task_runner_thread.h"
 #include "test/task_runner_thread_delegates.h"
+#include "test/test_helper.h"
 
 namespace perfetto {
 namespace shm_fuzz {
 
-static const char* kProducerSocket = tempnam("/tmp", "perfetto-producer");
-static const char* kConsumerSocket = tempnam("/tmp", "perfetto-consumer");
+// If we're building on Android and starting the daemons ourselves,
+// create the sockets in a world-writable location.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
+#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
+#else
+#define TEST_PRODUCER_SOCK_NAME PERFETTO_PRODUCER_SOCK_NAME
+#endif
 
 // Fake producer writing a protozero message of data into shared memory
 // buffer, followed by a sentinel message to signal completion to the
@@ -57,14 +64,14 @@
         on_produced_and_committed_(on_produced_and_committed) {}
 
   void Connect(const char* socket_name, base::TaskRunner* task_runner) {
-    endpoint_ = ProducerIPCClient::Connect(socket_name, this, task_runner);
+    endpoint_ = ProducerIPCClient::Connect(
+        socket_name, this, "android.perfetto.FakeProducer", task_runner);
   }
 
   void OnConnect() override {
     DataSourceDescriptor descriptor;
     descriptor.set_name(name_);
-    endpoint_->RegisterDataSource(descriptor,
-                                  [this](DataSourceID id) { id_ = id; });
+    endpoint_->RegisterDataSource(descriptor);
   }
 
   void OnDisconnect() override {}
@@ -95,7 +102,6 @@
   const std::string name_;
   const uint8_t* data_;
   const size_t size_;
-  DataSourceID id_ = 0;
   std::unique_ptr<Service::ProducerEndpoint> endpoint_;
   std::function<void()> on_produced_and_committed_;
 };
@@ -113,7 +119,7 @@
   void Initialize(base::TaskRunner* task_runner) override {
     producer_.reset(new FakeProducer("android.perfetto.FakeProducer", data_,
                                      size_, on_produced_and_committed_));
-    producer_->Connect(kProducerSocket, task_runner);
+    producer_->Connect(TEST_PRODUCER_SOCK_NAME, task_runner);
   }
 
  private:
@@ -128,9 +134,8 @@
 int FuzzSharedMemory(const uint8_t* data, size_t size) {
   base::TestTaskRunner task_runner;
 
-  TaskRunnerThread service_thread("perfetto.svc");
-  service_thread.Start(std::unique_ptr<ServiceDelegate>(
-      new ServiceDelegate(kProducerSocket, kConsumerSocket)));
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
 
   auto on_produced_and_committed =
       task_runner.CreateCheckpoint("produced.and.committed");
@@ -142,40 +147,29 @@
   producer_thread.Start(std::unique_ptr<FakeProducerDelegate>(
       new FakeProducerDelegate(data, size, posted_on_produced_and_committed)));
 
-  // Setup the TraceConfig for the consumer.
+  helper.ConnectConsumer();
+
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(8);
 
-  // Create the buffer for the fake producer.
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
   ds_config->set_name("android.perfetto.FakeProducer");
   ds_config->set_target_buffer(0);
 
-  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
-  auto on_consumer_data = [&on_readback_complete](
-                              std::vector<TracePacket> packets, bool has_more) {
-    for (auto& p : packets) {
-      p.Decode();
-      if (p->for_testing().str() == "end")
-        on_readback_complete();
-    }
-  };
-
-  auto on_connect = task_runner.CreateCheckpoint("consumer.connected");
-  FakeConsumer consumer(trace_config, std::move(on_connect),
-                        std::move(on_consumer_data), &task_runner);
-
-  consumer.Connect(kConsumerSocket);
-  task_runner.RunUntilCheckpoint("consumer.connected");
-
-  consumer.EnableTracing();
+  auto producer_enabled = task_runner.CreateCheckpoint("producer.enabled");
+  task_runner.PostTask(producer_enabled);
+  helper.StartTracing(trace_config);
   task_runner.RunUntilCheckpoint("produced.and.committed");
 
-  consumer.ReadTraceData();
+  auto on_readback_complete = task_runner.CreateCheckpoint("readback.complete");
+  auto on_consumer_data =
+      [&on_readback_complete](const TracePacket::DecodedTracePacket& packet) {
+        if (packet.for_testing().str() == "end")
+          on_readback_complete();
+      };
+  helper.ReadData(on_consumer_data, [] {});
   task_runner.RunUntilCheckpoint("readback.complete");
 
-  consumer.Disconnect();
-
   return 0;
 }
 
diff --git a/test/fake_consumer.cc b/test/fake_consumer.cc
deleted file mode 100644
index d1837c7..0000000
--- a/test/fake_consumer.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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 "test/fake_consumer.h"
-
-#include <utility>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "perfetto/base/logging.h"
-#include "perfetto/trace/test_event.pbzero.h"
-#include "perfetto/trace/trace_packet.pbzero.h"
-#include "perfetto/traced/traced.h"
-#include "perfetto/tracing/core/trace_packet.h"
-#include "perfetto/tracing/core/trace_writer.h"
-
-namespace perfetto {
-
-FakeConsumer::FakeConsumer(
-    const TraceConfig& trace_config,
-    std::function<void()> on_connect,
-    std::function<void(std::vector<TracePacket>, bool)> packet_callback,
-    base::TaskRunner* task_runner)
-    : task_runner_(task_runner),
-      trace_config_(trace_config),
-      on_connect_(on_connect),
-      packet_callback_(std::move(packet_callback)) {}
-FakeConsumer::~FakeConsumer() = default;
-
-void FakeConsumer::Connect(const char* socket_name) {
-  endpoint_ = ConsumerIPCClient::Connect(socket_name, this, task_runner_);
-}
-
-void FakeConsumer::Disconnect() {
-  endpoint_.reset();
-}
-
-void FakeConsumer::OnConnect() {
-  on_connect_();
-}
-
-void FakeConsumer::EnableTracing() {
-  endpoint_->EnableTracing(trace_config_);
-}
-
-void FakeConsumer::FreeBuffers() {
-  endpoint_->FreeBuffers();
-}
-
-void FakeConsumer::ReadTraceData() {
-  endpoint_->ReadBuffers();
-}
-
-void FakeConsumer::OnDisconnect() {
-  FAIL() << "Consumer unexpectedly disconnected from the service";
-}
-
-void FakeConsumer::OnTracingStop() {}
-
-void FakeConsumer::OnTraceData(std::vector<TracePacket> data, bool has_more) {
-  packet_callback_(std::move(data), has_more);
-}
-
-}  // namespace perfetto
diff --git a/test/fake_consumer.h b/test/fake_consumer.h
deleted file mode 100644
index d36dbba..0000000
--- a/test/fake_consumer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 TEST_FAKE_CONSUMER_H_
-#define TEST_FAKE_CONSUMER_H_
-
-#include <memory>
-#include <vector>
-
-#include "perfetto/tracing/core/consumer.h"
-#include "perfetto/tracing/core/trace_config.h"
-#include "perfetto/tracing/core/trace_packet.h"
-#include "perfetto/tracing/ipc/consumer_ipc_client.h"
-
-#include "src/base/test/test_task_runner.h"
-
-namespace perfetto {
-
-class FakeConsumer : public Consumer {
- public:
-  FakeConsumer(
-      const TraceConfig& trace_config,
-      std::function<void()> on_connect,
-      std::function<void(std::vector<TracePacket>, bool)> packet_callback,
-      base::TaskRunner* task_runner);
-  ~FakeConsumer() override;
-
-  void EnableTracing();
-  void FreeBuffers();
-  void Connect(const char* socket_name);
-  void Disconnect();
-  void ReadTraceData();
-
-  // Consumer implementation.
-  void OnConnect() override;
-  void OnDisconnect() override;
-  void OnTracingStop() override;
-  void OnTraceData(std::vector<TracePacket> packets, bool has_more) override;
-
- private:
-  base::TaskRunner* const task_runner_;
-  const TraceConfig trace_config_;
-  std::function<void()> on_connect_;
-  std::function<void(std::vector<TracePacket>, bool)> packet_callback_;
-  std::unique_ptr<Service::ConsumerEndpoint> endpoint_;  // Keep last.
-};
-
-}  // namespace perfetto
-
-#endif  // TEST_FAKE_CONSUMER_H_
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index 9dadf52..a81f656 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -21,6 +21,7 @@
 
 #include "gtest/gtest.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/base/time.h"
 #include "perfetto/base/utils.h"
 #include "perfetto/trace/test_event.pbzero.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
@@ -40,7 +41,7 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   task_runner_ = task_runner;
   endpoint_ = ProducerIPCClient::Connect(
-      socket_name, this, "com.google.perfetto.fake_producer", task_runner);
+      socket_name, this, "android.perfetto.FakeProducer", task_runner);
   on_create_data_source_instance_ = std::move(on_create_data_source_instance);
 }
 
@@ -48,8 +49,7 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   DataSourceDescriptor descriptor;
   descriptor.set_name(name_);
-  endpoint_->RegisterDataSource(descriptor,
-                                [this](DataSourceID id) { id_ = id; });
+  endpoint_->RegisterDataSource(descriptor);
 }
 
 void FakeProducer::OnDisconnect() {
@@ -66,6 +66,8 @@
   rnd_engine_ = std::minstd_rand0(source_config.for_testing().seed());
   message_count_ = source_config.for_testing().message_count();
   message_size_ = source_config.for_testing().message_size();
+  max_messages_per_second_ =
+      source_config.for_testing().max_messages_per_second();
   task_runner_->PostTask(on_create_data_source_instance_);
 }
 
@@ -78,18 +80,39 @@
 void FakeProducer::ProduceEventBatch(std::function<void()> callback) {
   task_runner_->PostTask([this, callback] {
     PERFETTO_CHECK(trace_writer_);
-
-    size_t payload_size = message_size_ - sizeof(uint32_t);
-    PERFETTO_CHECK(payload_size >= sizeof(char));
-
+    PERFETTO_CHECK(message_size_ > 1);
     std::unique_ptr<char, base::FreeDeleter> payload(
-        static_cast<char*>(malloc(payload_size)));
-    memset(payload.get(), '.', payload_size);
-    payload.get()[payload_size - 1] = 0;
-    for (size_t i = 0; i < message_count_; i++) {
-      auto handle = trace_writer_->NewTracePacket();
-      handle->set_for_testing()->set_seq_value(rnd_engine_());
-      handle->set_for_testing()->set_str(payload.get(), payload_size);
+        static_cast<char*>(malloc(message_size_)));
+    memset(payload.get(), '.', message_size_);
+    payload.get()[message_size_ - 1] = 0;
+
+    base::TimeMillis start = base::GetWallTimeMs();
+    int64_t iterations = 0;
+    size_t messages_to_emit = message_count_;
+    while (messages_to_emit > 0) {
+      size_t messages_in_minibatch =
+          max_messages_per_second_ == 0
+              ? messages_to_emit
+              : std::min(max_messages_per_second_, messages_to_emit);
+      PERFETTO_DCHECK(messages_to_emit >= messages_in_minibatch);
+
+      for (size_t i = 0; i < messages_in_minibatch; i++) {
+        auto handle = trace_writer_->NewTracePacket();
+        handle->set_for_testing()->set_seq_value(rnd_engine_());
+        handle->set_for_testing()->set_str(payload.get(), message_size_);
+      }
+      messages_to_emit -= messages_in_minibatch;
+
+      // Pause until the second boundary to make sure that we are adhering to
+      // the speed limitation.
+      if (max_messages_per_second_ > 0) {
+        int64_t expected_time_taken = ++iterations * 1000;
+        base::TimeMillis time_taken = base::GetWallTimeMs() - start;
+        while (time_taken.count() < expected_time_taken) {
+          usleep((expected_time_taken - time_taken.count()) * 1000);
+          time_taken = base::GetWallTimeMs() - start;
+        }
+      }
     }
     trace_writer_->Flush(callback);
   });
diff --git a/test/fake_producer.h b/test/fake_producer.h
index 568c09b..2495518 100644
--- a/test/fake_producer.h
+++ b/test/fake_producer.h
@@ -58,10 +58,10 @@
   base::ThreadChecker thread_checker_;
   base::TaskRunner* task_runner_ = nullptr;
   std::string name_;
-  DataSourceID id_ = 0;
   std::minstd_rand0 rnd_engine_;
   size_t message_size_ = 0;
   size_t message_count_ = 0;
+  size_t max_messages_per_second_ = 0;
   std::function<void()> on_create_data_source_instance_;
   std::unique_ptr<Service::ProducerEndpoint> endpoint_;
   std::unique_ptr<TraceWriter> trace_writer_;
diff --git a/test/test_helper.cc b/test/test_helper.cc
new file mode 100644
index 0000000..24f814b
--- /dev/null
+++ b/test/test_helper.cc
@@ -0,0 +1,112 @@
+/*
+ * 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 "test/test_helper.h"
+
+#include "gtest/gtest.h"
+#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+#include "perfetto/traced/traced.h"
+#include "perfetto/tracing/core/trace_packet.h"
+#include "test/task_runner_thread_delegates.h"
+
+namespace perfetto {
+
+// If we're building on Android and starting the daemons ourselves,
+// create the sockets in a world-writable location.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
+#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
+#define TEST_CONSUMER_SOCK_NAME "/data/local/tmp/traced_consumer"
+#else
+#define TEST_PRODUCER_SOCK_NAME PERFETTO_PRODUCER_SOCK_NAME
+#define TEST_CONSUMER_SOCK_NAME PERFETTO_CONSUMER_SOCK_NAME
+#endif
+
+TestHelper::TestHelper(base::TestTaskRunner* task_runner)
+    : task_runner_(task_runner),
+      service_thread_("perfetto.svc"),
+      producer_thread_("perfetto.prd") {}
+
+void TestHelper::OnConnect() {
+  std::move(continuation_callack_)();
+}
+
+void TestHelper::OnDisconnect() {
+  FAIL() << "Consumer unexpectedly disconnected from the service";
+}
+
+void TestHelper::OnTracingStop() {}
+
+void TestHelper::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
+  for (auto& packet : packets) {
+    ASSERT_TRUE(packet.Decode());
+    if (packet->has_clock_snapshot() || packet->has_trace_config())
+      continue;
+    ASSERT_EQ(protos::TracePacket::kTrustedUid,
+              packet->optional_trusted_uid_case());
+    packet_callback_(*packet);
+  }
+
+  if (!has_more) {
+    packet_callback_ = {};
+    std::move(continuation_callack_)();
+  }
+}
+
+void TestHelper::StartServiceIfRequired() {
+#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
+  service_thread_.Start(std::unique_ptr<ServiceDelegate>(
+      new ServiceDelegate(TEST_PRODUCER_SOCK_NAME, TEST_CONSUMER_SOCK_NAME)));
+#endif
+}
+
+FakeProducer* TestHelper::ConnectFakeProducer() {
+  std::unique_ptr<FakeProducerDelegate> producer_delegate(
+      new FakeProducerDelegate(
+          TEST_PRODUCER_SOCK_NAME,
+          WrapTask(task_runner_->CreateCheckpoint("producer.enabled"))));
+  FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
+  producer_thread_.Start(std::move(producer_delegate));
+  return producer_delegate_cached->producer();
+}
+
+void TestHelper::ConnectConsumer() {
+  continuation_callack_ = task_runner_->CreateCheckpoint("consumer.connected");
+  endpoint_ =
+      ConsumerIPCClient::Connect(TEST_CONSUMER_SOCK_NAME, this, task_runner_);
+  task_runner_->RunUntilCheckpoint("consumer.connected");
+}
+
+void TestHelper::StartTracing(const TraceConfig& config) {
+  endpoint_->EnableTracing(config);
+  task_runner_->RunUntilCheckpoint("producer.enabled");
+}
+
+void TestHelper::ReadData(
+    std::function<void(const TracePacket::DecodedTracePacket&)> packet_callback,
+    std::function<void()> on_finish_callback) {
+  packet_callback_ = packet_callback;
+  continuation_callack_ = on_finish_callback;
+  endpoint_->ReadBuffers();
+}
+
+std::function<void()> TestHelper::WrapTask(
+    const std::function<void()>& function) {
+  return [this, function] { task_runner_->PostTask(function); };
+}
+
+}  // namespace perfetto
diff --git a/test/test_helper.h b/test/test_helper.h
new file mode 100644
index 0000000..87a9be5
--- /dev/null
+++ b/test/test_helper.h
@@ -0,0 +1,66 @@
+/*
+ * 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 TEST_TEST_HELPER_H_
+#define TEST_TEST_HELPER_H_
+
+#include "perfetto/tracing/core/consumer.h"
+#include "perfetto/tracing/core/trace_config.h"
+#include "perfetto/tracing/core/trace_packet.h"
+#include "perfetto/tracing/ipc/consumer_ipc_client.h"
+#include "src/base/test/test_task_runner.h"
+#include "test/fake_producer.h"
+#include "test/task_runner_thread.h"
+
+namespace perfetto {
+
+class TestHelper : public Consumer {
+ public:
+  explicit TestHelper(base::TestTaskRunner* task_runner);
+
+  // Consumer implementation.
+  void OnConnect() override;
+  void OnDisconnect() override;
+  void OnTracingStop() override;
+  void OnTraceData(std::vector<TracePacket> packets, bool has_more) override;
+
+  void StartServiceIfRequired();
+  FakeProducer* ConnectFakeProducer();
+  void ConnectConsumer();
+  void StartTracing(const TraceConfig& config);
+  void ReadData(std::function<void(const TracePacket::DecodedTracePacket&)>
+                    packet_callback,
+                std::function<void()> on_finish_callback);
+
+  std::function<void()> WrapTask(const std::function<void()>& function);
+
+  TaskRunnerThread* service_thread() { return &service_thread_; }
+  TaskRunnerThread* producer_thread() { return &producer_thread_; }
+
+ private:
+  base::TestTaskRunner* task_runner_ = nullptr;
+
+  std::function<void(const TracePacket::DecodedTracePacket&)> packet_callback_;
+  std::function<void()> continuation_callack_;
+
+  TaskRunnerThread service_thread_;
+  TaskRunnerThread producer_thread_;
+  std::unique_ptr<Service::ConsumerEndpoint> endpoint_;  // Keep last.
+};
+
+}  // namespace perfetto
+
+#endif  // TEST_TEST_HELPER_H_
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
index 4a1df78..3c25934 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen.cc
@@ -103,10 +103,10 @@
   }
 }
 
-void PrintTraceToTextMain(const std::set<std::string>& events) {
+void PrintEventFormatterMain(const std::set<std::string>& events) {
   printf(
-      "\nAdd output to TraceToSystrace for loop in "
-      "tools/ftrace_proto_gen/main.cc\n");
+      "\nAdd output to FormatEventText in "
+      "tools/ftrace_proto_gen/ftrace_event_formatter.cc\n");
   for (auto event : events) {
     printf(
         "else if (event.has_%s()) {\nconst auto& inner = event.%s();\nline = "
@@ -115,17 +115,30 @@
   }
 }
 
-void PrintTraceToTextUsingStatements(const std::set<std::string>& events) {
-  printf("\nAdd output to tools/ftrace_proto_gen/main.cc\n");
+// Add output to ParseInode in ftrace_inode_handler
+void PrintInodeHandlerMain(const std::string& event_name,
+                           const perfetto::Proto& proto) {
+  for (const auto& field : proto.fields) {
+    if (Contains(field.name, "ino") && !Contains(field.name, "minor"))
+      printf(
+          "else if (event.has_%s() && event.%s().%s()) {\n*inode = "
+          "static_cast<uint64_t>(event.%s().%s());\n return true;\n} ",
+          event_name.c_str(), event_name.c_str(), field.name.c_str(),
+          event_name.c_str(), field.name.c_str());
+  }
+}
+
+void PrintEventFormatterUsingStatements(const std::set<std::string>& events) {
+  printf("\nAdd output to tools/ftrace_proto_gen/ftrace_event_formatter.cc\n");
   for (auto event : events) {
     printf("using protos::%sFtraceEvent;\n", ToCamelCase(event).c_str());
   }
 }
 
-void PrintTraceToTextFunctions(const std::set<std::string>& events) {
+void PrintEventFormatterFunctions(const std::set<std::string>& events) {
   printf(
-      "\nAdd output to tools/ftrace_proto_gen/main.cc and then manually go "
-      "through format files to match fields\n");
+      "\nAdd output to tools/ftrace_proto_gen/ftrace_event_formatter.cc and "
+      "then manually go through format files to match fields\n");
   for (auto event : events) {
     printf(
         "std::string Format%s(const %sFtraceEvent& event) {"
diff --git a/tools/ftrace_proto_gen/ftrace_proto_gen.h b/tools/ftrace_proto_gen/ftrace_proto_gen.h
index a5be4f4..11cf5cf 100644
--- a/tools/ftrace_proto_gen/ftrace_proto_gen.h
+++ b/tools/ftrace_proto_gen/ftrace_proto_gen.h
@@ -38,9 +38,11 @@
 };
 
 void PrintFtraceEventProtoAdditions(const std::set<std::string>& events);
-void PrintTraceToTextMain(const std::set<std::string>& events);
-void PrintTraceToTextUsingStatements(const std::set<std::string>& events);
-void PrintTraceToTextFunctions(const std::set<std::string>& events);
+void PrintEventFormatterMain(const std::set<std::string>& events);
+void PrintEventFormatterUsingStatements(const std::set<std::string>& events);
+void PrintEventFormatterFunctions(const std::set<std::string>& events);
+void PrintInodeHandlerMain(const std::string& event_name,
+                           const perfetto::Proto& proto);
 
 bool GenerateProto(const FtraceEvent& format, Proto* proto_out);
 std::string InferProtoType(const FtraceEvent::Field& field);
diff --git a/tools/ftrace_proto_gen/main.cc b/tools/ftrace_proto_gen/main.cc
index 33d8abb..c701d39 100644
--- a/tools/ftrace_proto_gen/main.cc
+++ b/tools/ftrace_proto_gen/main.cc
@@ -62,9 +62,12 @@
 
   if (!new_events.empty()) {
     perfetto::PrintFtraceEventProtoAdditions(new_events);
-    perfetto::PrintTraceToTextMain(new_events);
-    perfetto::PrintTraceToTextUsingStatements(new_events);
-    perfetto::PrintTraceToTextFunctions(new_events);
+    perfetto::PrintEventFormatterMain(new_events);
+    perfetto::PrintEventFormatterUsingStatements(new_events);
+    perfetto::PrintEventFormatterFunctions(new_events);
+    printf(
+        "\nAdd output to ParseInode in "
+        "tools/ftrace_proto_gen/ftrace_inode_handler.cc\n");
   }
 
   for (auto event : events) {
@@ -105,6 +108,9 @@
       return 1;
     }
 
+    if (!new_events.empty())
+      PrintInodeHandlerMain(format.name, proto);
+
     events_info.push_back(
         perfetto::SingleEventInfo(format, proto, group, proto_field_id));
 
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 728ed23..f71e001 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -43,6 +43,7 @@
     '//:perfetto',
     '//:traced',
     '//:traced_probes',
+    '//:trace_to_text',
 ]
 
 # Defines a custom init_rc argument to be applied to the corresponding output
@@ -55,6 +56,10 @@
     '//:perfetto_trace_protos',
 ]
 
+target_host_only = [
+    '//:trace_to_text',
+]
+
 # Arguments for the GN output directory.
 gn_args = 'target_os="android" target_cpu="arm" is_debug=false build_with_android=true'
 
@@ -470,7 +475,7 @@
     """
     target = desc[target_name]
     if target['type'] == 'executable':
-        if 'host' in target['toolchain']:
+        if 'host' in target['toolchain'] or target_name in target_host_only:
             module_type = 'cc_binary_host'
         elif target.get('testonly'):
             module_type = 'cc_test'
diff --git a/tools/gen_merged_trace_config b/tools/gen_merged_trace_config
index 9a7abba..135fd02 100755
--- a/tools/gen_merged_trace_config
+++ b/tools/gen_merged_trace_config
@@ -20,6 +20,8 @@
 
 PROTOS = (
   'protos/perfetto/config/chrome/chrome_config.proto',
+  'protos/perfetto/config/inode_file/inode_file_config.proto',
+  'protos/perfetto/config/process_stats/process_stats_config.proto',
   'protos/perfetto/config/data_source_config.proto',
   'protos/perfetto/config/ftrace/ftrace_config.proto',
   'protos/perfetto/config/test_config.proto',
diff --git a/tools/gen_tracing_cpp_headers_from_protos.py b/tools/gen_tracing_cpp_headers_from_protos.py
index 350c5ad..ce7fdb6 100755
--- a/tools/gen_tracing_cpp_headers_from_protos.py
+++ b/tools/gen_tracing_cpp_headers_from_protos.py
@@ -20,6 +20,8 @@
 PROTOS = (
   'perfetto/config/chrome/chrome_config.proto',
   'perfetto/config/data_source_config.proto',
+  'perfetto/config/inode_file/inode_file_config.proto',
+  'perfetto/config/process_stats/process_stats_config.proto',
   'perfetto/config/data_source_descriptor.proto',
   'perfetto/config/ftrace/ftrace_config.proto',
   'perfetto/config/trace_config.proto',
diff --git a/tools/proto_to_cpp/proto_to_cpp.cc b/tools/proto_to_cpp/proto_to_cpp.cc
index 82fe8c0..6696c57 100644
--- a/tools/proto_to_cpp/proto_to_cpp.cc
+++ b/tools/proto_to_cpp/proto_to_cpp.cc
@@ -451,7 +451,9 @@
         p->Print("  auto* entry = proto->add_$n$();\n", "n", field->name());
         p->Print("  it.ToProto(entry);\n");
       } else {
-        p->Print("  proto->add_$n$(it);\n", "n", field->name());
+        p->Print(
+            "  proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
+            "n", field->name());
         p->Print(
             "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
             "mismatch\");\n",
diff --git a/tools/run_android_test b/tools/run_android_test
index bb92156..83f029d 100755
--- a/tools/run_android_test
+++ b/tools/run_android_test
@@ -114,6 +114,7 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--no-cleanup', '-n', action='store_true')
   parser.add_argument('--no-data-deps', '-x', action='store_true')
+  parser.add_argument('--env', '-e', action='append')
   parser.add_argument('out_dir', help='out/android/')
   parser.add_argument('test_name', help='perfetto_unittests')
   parser.add_argument('cmd_args', nargs=argparse.REMAINDER)
@@ -138,10 +139,10 @@
 
   # LLVM sanitizers require to sideload a libclangrtXX.so on the device.
   sanitizer_libs = os.path.join(args.out_dir, 'sanitizer_libs')
-  env = ''
+  env = ' '.join(args.env if args.env is not None else []) + ' '
   if os.path.exists(sanitizer_libs):
     AdbCall('push', sanitizer_libs, target_dir)
-    env = 'LD_LIBRARY_PATH="%s/sanitizer_libs" ' % (target_dir)
+    env += 'LD_LIBRARY_PATH="%s/sanitizer_libs" ' % (target_dir)
   cmd = 'cd %s;' % target_dir;
   binary = env + './%s' % args.test_name
   cmd += binary
diff --git a/tools/tmux b/tools/tmux
index 568fc78..1e4b3d4 100755
--- a/tools/tmux
+++ b/tools/tmux
@@ -164,8 +164,12 @@
 tmux select-pane -t 2
 
 tmux -2 attach-session -t demo
-reset
-TRACE=$HOME/Downloads/trace.json
-echo -e "\n\x1b[32mPulling trace into $TRACE\x1b[0m"
-pull trace $TMPDIR/trace.protobuf
-$OUT/trace_to_text systrace < $TMPDIR/trace.protobuf > $TRACE
+
+reset_tracing
+
+TRACE=$HOME/Downloads/trace
+pull trace /tmp/trace.protobuf
+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"
+$OUT/trace_to_text systrace < /tmp/trace.protobuf > $TRACE.json
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 90e1233c..5de6da9 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -12,33 +12,48 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../../gn/perfetto.gni")
+
+source_set("lib") {
+  testonly = true
+  deps = [
+    "../../gn:default_deps",
+    "../../gn:protobuf_full_deps",
+    "../../protos/perfetto/trace:lite",
+  ]
+  sources = [
+    "ftrace_event_formatter.cc",
+    "ftrace_event_formatter.h",
+    "ftrace_inode_handler.cc",
+    "ftrace_inode_handler.h",
+    "main.cc",
+  ]
+}
+
 if (current_toolchain == host_toolchain) {
   executable("trace_to_text_host") {
     testonly = true
     deps = [
+      ":lib",
       "../../gn:default_deps",
-      "../../gn:protobuf_full_deps",
-      "../../protos/perfetto/trace:lite",
-    ]
-    sources = [
-      "ftrace_event_formatter.cc",
-      "ftrace_event_formatter.h",
-      "main.cc",
     ]
   }
 }
 
-copy("trace_to_text") {
-  testonly = true
-  host_out_dir_ =
-      get_label_info(":trace_to_text_host($host_toolchain)", "root_out_dir")
-  deps = [
-    ":trace_to_text_host($host_toolchain)",
-  ]
-  sources = [
-    "${host_out_dir_}/trace_to_text_host",
-  ]
-  outputs = [
-    "${root_out_dir}/trace_to_text",
-  ]
+# The one for the android tree is defined in the top-level BUILD.gn.
+if (!build_with_android) {
+  copy("trace_to_text") {
+    testonly = true
+    host_out_dir_ =
+        get_label_info(":trace_to_text_host($host_toolchain)", "root_out_dir")
+    deps = [
+      ":trace_to_text_host($host_toolchain)",
+    ]
+    sources = [
+      "${host_out_dir_}/trace_to_text_host",
+    ]
+    outputs = [
+      "${root_out_dir}/trace_to_text",
+    ]
+  }
 }
diff --git a/tools/trace_to_text/ftrace_event_formatter.cc b/tools/trace_to_text/ftrace_event_formatter.cc
index 1120d7a..bdf7d88 100644
--- a/tools/trace_to_text/ftrace_event_formatter.cc
+++ b/tools/trace_to_text/ftrace_event_formatter.cc
@@ -17,6 +17,15 @@
 #include "tools/trace_to_text/ftrace_event_formatter.h"
 
 #include <inttypes.h>
+#include <algorithm>
+#include <string>
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/sysmacros.h>  // For major() / minor()
+#endif
 
 namespace perfetto {
 namespace {
@@ -370,7 +379,7 @@
   sprintf(line,
           "sched_switch: prev_comm=%s "
           "prev_pid=%d prev_prio=%d prev_state=%s ==> next_comm=%s next_pid=%d "
-          "next_prio=%d\\n",
+          "next_prio=%d",
           sched_switch.prev_comm().c_str(), sched_switch.prev_pid(),
           sched_switch.prev_prio(),
           GetSchedSwitchFlag(sched_switch.prev_state()),
@@ -383,7 +392,7 @@
   char line[2048];
   sprintf(line,
           "sched_wakeup: comm=%s "
-          "pid=%d prio=%d success=%d target_cpu=%03d\\n",
+          "pid=%d prio=%d success=%d target_cpu=%03d",
           sched_wakeup.comm().c_str(), sched_wakeup.pid(), sched_wakeup.prio(),
           sched_wakeup.success(), sched_wakeup.target_cpu());
   return std::string(line);
@@ -392,24 +401,31 @@
 std::string FormatSchedBlockedReason(
     const SchedBlockedReasonFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_blocked_reason: pid=%d iowait=%d caller=%llxS\\n",
+  sprintf(line, "sched_blocked_reason: pid=%d iowait=%d caller=%llxS",
           event.pid(), event.io_wait(), event.caller());
   return std::string(line);
 }
 
 std::string FormatPrint(const PrintFtraceEvent& print) {
-  char line[2048];
-  std::string msg = print.buf();
+  std::string line = "tracing_mark_write: ";
+  size_t dst = line.size();
+  line.resize(2048);
+  const std::string& msg = print.buf();
+
   // Remove any newlines in the message. It's not entirely clear what the right
   // behaviour is here. Maybe we should escape them instead?
-  msg.erase(std::remove(msg.begin(), msg.end(), '\n'), msg.end());
-  sprintf(line, "tracing_mark_write: %s\\n", msg.c_str());
-  return std::string(line);
+  for (size_t src = 0; src < msg.size() && dst < line.size() - 1; src++) {
+    char c = msg[src];
+    if (c != '\n')
+      line[dst++] = c;
+  }
+  line.resize(dst);
+  return line;
 }
 
 std::string FormatCpuFrequency(const CpuFrequencyFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "cpu_frequency: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n",
+  sprintf(line, "cpu_frequency: state=%" PRIu32 " cpu_id=%" PRIu32,
           event.state(), event.cpu_id());
   return std::string(line);
 }
@@ -419,21 +435,21 @@
   char line[2048];
   sprintf(line,
           "cpu_frequency_limits: min_freq=%" PRIu32 "max_freq=%" PRIu32
-          " cpu_id=%" PRIu32 "\\n",
+          " cpu_id=%" PRIu32,
           event.min_freq(), event.max_freq(), event.cpu_id());
   return std::string(line);
 }
 
 std::string FormatCpuIdle(const CpuIdleFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "cpu_idle: state=%" PRIu32 " cpu_id=%" PRIu32 "\\n",
-          event.state(), event.cpu_id());
+  sprintf(line, "cpu_idle: state=%" PRIu32 " cpu_id=%" PRIu32, event.state(),
+          event.cpu_id());
   return std::string(line);
 }
 
 std::string FormatClockSetRate(const ClockSetRateFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "clock_set_rate: %s state=%llu cpu_id=%llu\\n",
+  sprintf(line, "clock_set_rate: %s state=%llu cpu_id=%llu",
           event.name().empty() ? "todo" : event.name().c_str(), event.state(),
           event.cpu_id());
   return std::string(line);
@@ -441,7 +457,7 @@
 
 std::string FormatClockEnable(const ClockEnableFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "clock_enable: %s state=%llu cpu_id=%llu\\n",
+  sprintf(line, "clock_enable: %s state=%llu cpu_id=%llu",
           event.name().empty() ? "todo" : event.name().c_str(), event.state(),
           event.cpu_id());
   return std::string(line);
@@ -449,7 +465,7 @@
 
 std::string FormatClockDisable(const ClockDisableFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "clock_disable: %s state=%llu cpu_id=%llu\\n",
+  sprintf(line, "clock_disable: %s state=%llu cpu_id=%llu",
           event.name().empty() ? "todo" : event.name().c_str(), event.state(),
           event.cpu_id());
   return std::string(line);
@@ -457,27 +473,26 @@
 
 std::string FormatTracingMarkWrite(const TracingMarkWriteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "tracing_mark_write: %s|%d|%s\\n",
-          event.trace_begin() ? "B" : "E", event.pid(),
-          event.trace_name().c_str());
+  sprintf(line, "tracing_mark_write: %s|%d|%s", event.trace_begin() ? "B" : "E",
+          event.pid(), event.trace_name().c_str());
   return std::string(line);
 }
 
 std::string FormatBinderLocked(const BinderLockedFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "binder_locked: tag=%s\\n", event.tag().c_str());
+  sprintf(line, "binder_locked: tag=%s", event.tag().c_str());
   return std::string(line);
 }
 
 std::string FormatBinderUnlock(const BinderUnlockFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "binder_unlock: tag=%s\\n", event.tag().c_str());
+  sprintf(line, "binder_unlock: tag=%s", event.tag().c_str());
   return std::string(line);
 }
 
 std::string FormatBinderLock(const BinderLockFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "binder_lock: tag=%s\\n", event.tag().c_str());
+  sprintf(line, "binder_lock: tag=%s", event.tag().c_str());
   return std::string(line);
 }
 
@@ -485,7 +500,7 @@
   char line[2048];
   sprintf(line,
           "binder_transaction: transaction=%d dest_node=%d dest_proc=%d "
-          "dest_thread=%d reply=%d flags=0x%x code=0x%x\\n",
+          "dest_thread=%d reply=%d flags=0x%x code=0x%x",
           event.debug_id(), event.target_node(), event.to_proc(),
           event.to_thread(), event.reply(), event.flags(), event.code());
   return std::string(line);
@@ -494,7 +509,7 @@
 std::string FormatBinderTransactionReceived(
     const BinderTransactionReceivedFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "binder_transaction_received: transaction=%d\\n",
+  sprintf(line, "binder_transaction_received: transaction=%d",
           event.debug_id());
   return std::string(line);
 }
@@ -502,7 +517,7 @@
 std::string FormatExt4SyncFileEnter(const Ext4SyncFileEnterFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_sync_file_enter: dev %d,%d ino %lu parent %lu datasync %d \\n",
+          "ext4_sync_file_enter: dev %d,%d ino %lu parent %lu datasync %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.parent(), event.datasync());
   return std::string(line);
@@ -510,7 +525,7 @@
 
 std::string FormatExt4SyncFileExit(const Ext4SyncFileExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_sync_file_exit: dev %d,%d ino %lu ret %d\\n",
+  sprintf(line, "ext4_sync_file_exit: dev %d,%d ino %lu ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.ret());
   return std::string(line);
@@ -519,7 +534,7 @@
 std::string FormatExt4DaWriteBegin(const Ext4DaWriteBeginFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_da_write_begin: dev %d,%d ino %lu pos %lld len %u flags %u\\n",
+          "ext4_da_write_begin: dev %d,%d ino %lu pos %lld len %u flags %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.len(), event.flags());
   return std::string(line);
@@ -528,7 +543,7 @@
 std::string FormatExt4DaWriteEnd(const Ext4DaWriteEndFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_da_write_end: dev %d,%d ino %lu pos %lld len %u copied %u\\n",
+          "ext4_da_write_end: dev %d,%d ino %lu pos %lld len %u copied %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.len(), event.copied());
   return std::string(line);
@@ -536,39 +551,38 @@
 
 std::string FormatBlockRqIssue(const BlockRqIssueFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_issue: %d,%d %s %u (%s) %llu + %u [%s]\\n",
+  sprintf(line, "block_rq_issue: %d,%d %s %u (%s) %llu + %u [%s]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
           event.bytes(), event.cmd().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatI2cRead(const I2cReadFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "i2c_read: i2c-%d #%u a=%03x f=%04x l=%u\\n",
-          event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(),
-          event.len());
+  sprintf(line, "i2c_read: i2c-%d #%u a=%03x f=%04x l=%u", event.adapter_nr(),
+          event.msg_nr(), event.addr(), event.flags(), event.len());
   return std::string(line);
 }
 
 std::string FormatI2cResult(const I2cResultFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "i2c_result: i2c-%d n=%u ret=%d\\n", event.adapter_nr(),
+  sprintf(line, "i2c_result: i2c-%d n=%u ret=%d", event.adapter_nr(),
           event.nr_msgs(), event.ret());
   return std::string(line);
 }
 
 std::string FormatIrqHandlerEntry(const IrqHandlerEntryFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "irq_handler_entry: irq=%d name=%s\\n", event.irq(),
+  sprintf(line, "irq_handler_entry: irq=%d name=%s", event.irq(),
           event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatIrqHandlerExit(const IrqHandlerExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "irq_handler_exit: irq=%d ret=%s\\n", event.irq(),
+  sprintf(line, "irq_handler_exit: irq=%d ret=%s", event.irq(),
           event.ret() ? "handled" : "unhandled");
   return std::string(line);
 }
@@ -576,7 +590,7 @@
 std::string FormatMmVmscanKswapdWake(
     const MmVmscanKswapdWakeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_vmscan_kswapd_wake: nid=%d order=%d\\n", event.nid(),
+  sprintf(line, "mm_vmscan_kswapd_wake: nid=%d order=%d", event.nid(),
           event.order());
   return std::string(line);
 }
@@ -584,77 +598,76 @@
 std::string FormatMmVmscanKswapdSleep(
     const MmVmscanKswapdSleepFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_vmscan_kswapd_sleep: nid=%d\\n", event.nid());
+  sprintf(line, "mm_vmscan_kswapd_sleep: nid=%d", event.nid());
   return std::string(line);
 }
 
 std::string FormatRegulatorEnable(const RegulatorEnableFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_enable: name=%s\\n", event.name().c_str());
+  sprintf(line, "regulator_enable: name=%s", event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatRegulatorEnableDelay(
     const RegulatorEnableDelayFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_enable_delay: name=%s\\n", event.name().c_str());
+  sprintf(line, "regulator_enable_delay: name=%s", event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatRegulatorEnableComplete(
     const RegulatorEnableCompleteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_enable_complete: name=%s\\n", event.name().c_str());
+  sprintf(line, "regulator_enable_complete: name=%s", event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatRegulatorDisable(const RegulatorDisableFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_disable: name=%s\\n", event.name().c_str());
+  sprintf(line, "regulator_disable: name=%s", event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatRegulatorDisableComplete(
     const RegulatorDisableCompleteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_disable_complete: name=%s\\n", event.name().c_str());
+  sprintf(line, "regulator_disable_complete: name=%s", event.name().c_str());
   return std::string(line);
 }
 
 std::string FormatRegulatorSetVoltage(
     const RegulatorSetVoltageFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_set_voltage: name=%s (%d-%d)\\n",
-          event.name().c_str(), event.min(), event.max());
+  sprintf(line, "regulator_set_voltage: name=%s (%d-%d)", event.name().c_str(),
+          event.min(), event.max());
   return std::string(line);
 }
 
 std::string FormatRegulatorSetVoltageComplete(
     const RegulatorSetVoltageCompleteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "regulator_set_voltage_complete: name=%s, val=%u\\n",
+  sprintf(line, "regulator_set_voltage_complete: name=%s, val=%u",
           event.name().c_str(), event.val());
   return std::string(line);
 }
 
 std::string FormatSchedCpuHotplug(const SchedCpuHotplugFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_cpu_hotplug: cpu %d %s error=%d\\n",
-          event.affected_cpu(), event.status() ? "online" : "offline",
-          event.error());
+  sprintf(line, "sched_cpu_hotplug: cpu %d %s error=%d", event.affected_cpu(),
+          event.status() ? "online" : "offline", event.error());
   return std::string(line);
 }
 
 std::string FormatSyncTimeline(const SyncTimelineFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sync_timeline: name=%s value=%s\\n", event.name().c_str(),
+  sprintf(line, "sync_timeline: name=%s value=%s", event.name().c_str(),
           event.value().c_str());
   return std::string(line);
 }
 
 std::string FormatSyncWait(const SyncWaitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sync_wait: %s name=%s state=%d\\n",
+  sprintf(line, "sync_wait: %s name=%s state=%d",
           event.begin() ? "begin" : "end", event.name().c_str(),
           event.status());
   return std::string(line);
@@ -662,28 +675,28 @@
 
 std::string FormatSyncPt(const SyncPtFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sync_pt: name=%s value=%s\\n", event.timeline().c_str(),
+  sprintf(line, "sync_pt: name=%s value=%s", event.timeline().c_str(),
           event.value().c_str());
   return std::string(line);
 }
 
 std::string FormatSoftirqRaise(const SoftirqRaiseFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "softirq_raise: vec=%u [action=%s]\\n", event.vec(),
+  sprintf(line, "softirq_raise: vec=%u [action=%s]", event.vec(),
           SoftirqArray[event.vec()]);
   return std::string(line);
 }
 
 std::string FormatSoftirqEntry(const SoftirqEntryFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "softirq_entry: vec=%u [action=%s]\\n", event.vec(),
+  sprintf(line, "softirq_entry: vec=%u [action=%s]", event.vec(),
           SoftirqArray[event.vec()]);
   return std::string(line);
 }
 
 std::string FormatSoftirqExit(const SoftirqExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "softirq_exit: vec=%u [action=%s]\\n", event.vec(),
+  sprintf(line, "softirq_exit: vec=%u [action=%s]", event.vec(),
           SoftirqArray[event.vec()]);
   return std::string(line);
 }
@@ -691,18 +704,16 @@
 std::string FormatI2cWrite(const I2cWriteFtraceEvent& event) {
   char line[2048];
   // TODO(hjd): Check event.buf().
-  sprintf(line, "i2c_write: i2c-%d #%u a=%03x f=%04x l=%u\\n",
-          event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(),
-          event.len());
+  sprintf(line, "i2c_write: i2c-%d #%u a=%03x f=%04x l=%u", event.adapter_nr(),
+          event.msg_nr(), event.addr(), event.flags(), event.len());
   return std::string(line);
 }
 
 std::string FormatI2cReply(const I2cReplyFtraceEvent& event) {
   char line[2048];
   // TODO(hjd): Check event.buf().
-  sprintf(line, "i2c_reply: i2c-%d #%u a=%03x f=%04x l=%u\\n",
-          event.adapter_nr(), event.msg_nr(), event.addr(), event.flags(),
-          event.len());
+  sprintf(line, "i2c_reply: i2c-%d #%u a=%03x f=%04x l=%u", event.adapter_nr(),
+          event.msg_nr(), event.addr(), event.flags(), event.len());
   return std::string(line);
 }
 
@@ -710,7 +721,7 @@
 std::string FormatMmVmscanDirectReclaimBegin(
     const MmVmscanDirectReclaimBeginFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_vmscan_direct_reclaim_begin: order=%d may_writepage=%d\\n",
+  sprintf(line, "mm_vmscan_direct_reclaim_begin: order=%d may_writepage=%d",
           event.order(), event.may_writepage());
   return std::string(line);
 }
@@ -718,7 +729,7 @@
 std::string FormatMmVmscanDirectReclaimEnd(
     const MmVmscanDirectReclaimEndFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_vmscan_direct_reclaim_end: nr_reclaimed=%llu\\n",
+  sprintf(line, "mm_vmscan_direct_reclaim_end: nr_reclaimed=%llu",
           event.nr_reclaimed());
   return std::string(line);
 }
@@ -727,7 +738,7 @@
   char line[2048];
   sprintf(line,
           "lowmemory_kill: %s (%d), page cache %lldkB (limit %lldkB), free "
-          "%lldKb\\n",
+          "%lldKb",
           event.comm().c_str(), event.pid(), event.pagecache_size(),
           event.pagecache_limit(), event.free());
   return std::string(line);
@@ -736,7 +747,7 @@
 std::string FormatWorkqueueExecuteStart(
     const WorkqueueExecuteStartFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "workqueue_execute_start: work struct %llx: function %llxf\\n",
+  sprintf(line, "workqueue_execute_start: work struct %llx: function %llxf",
           event.work(), event.function());
   return std::string(line);
 }
@@ -744,7 +755,7 @@
 std::string FormatWorkqueueExecuteEnd(
     const WorkqueueExecuteEndFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "workqueue_execute_end: work struct %llx\\n", event.work());
+  sprintf(line, "workqueue_execute_end: work struct %llx", event.work());
   return std::string(line);
 }
 
@@ -754,7 +765,7 @@
   sprintf(
       line,
       "workqueue_queue_work: work struct=%llx function=%llxf workqueue=%llx "
-      "req_cpu=%u cpu=%u\\n",
+      "req_cpu=%u cpu=%u",
       event.work(), event.function(), event.workqueue(), event.req_cpu(),
       event.cpu());
   return std::string(line);
@@ -763,7 +774,7 @@
 std::string FormatWorkqueueActivateWork(
     const WorkqueueActivateWorkFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "workqueue_activate_work: work struct %llx\\n", event.work());
+  sprintf(line, "workqueue_activate_work: work struct %llx", event.work());
   return std::string(line);
 }
 
@@ -771,7 +782,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_begin: zone_start=0x%llx migrate_pfn=0x%llx "
-          "free_pfn=0x%llx zone_end=0x%llx, mode=%s\\n",
+          "free_pfn=0x%llx zone_end=0x%llx, mode=%s",
           event.zone_start(), event.migrate_pfn(), event.free_pfn(),
           event.zone_end(), event.sync() ? "sync" : "async");
   return std::string(line);
@@ -782,7 +793,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_defer_compaction: node=%d zone=%-8s order=%d "
-          "order_failed=%d consider=%u limit=%lu\\n",
+          "order_failed=%d consider=%u limit=%lu",
           event.nid(), MmCompactionSuitableArray[event.idx()], event.order(),
           event.order_failed(), event.considered(), 1UL << event.defer_shift());
   return std::string(line);
@@ -793,7 +804,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_deferred: node=%d zone=%-8s order=%d order_failed=%d "
-          "consider=%u limit=%lu\\n",
+          "consider=%u limit=%lu",
           event.nid(), MmCompactionSuitableArray[event.idx()], event.order(),
           event.order_failed(), event.considered(), 1UL << event.defer_shift());
   return std::string(line);
@@ -804,7 +815,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_defer_reset: node=%d zone=%-8s order=%d "
-          "order_failed=%d consider=%u limit=%lu\\n",
+          "order_failed=%d consider=%u limit=%lu",
           event.nid(), MmCompactionSuitableArray[event.idx()], event.order(),
           event.order_failed(), event.considered(), 1UL << event.defer_shift());
   return std::string(line);
@@ -814,7 +825,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_end: zone_start=0x%llx migrate_pfn=0x%llx "
-          "free_pfn=0x%llx zone_end=0x%llx, mode=%s status=%s\\n",
+          "free_pfn=0x%llx zone_end=0x%llx, mode=%s status=%s",
           event.zone_start(), event.migrate_pfn(), event.free_pfn(),
           event.zone_end(), event.sync() ? "sync" : "aysnc",
           MmCompactionRetArray[event.status()]);
@@ -824,7 +835,7 @@
 std::string FormatMmCompactionFinished(
     const MmCompactionFinishedFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_compaction_finished: node=%d zone=%-8s order=%d ret=%s\\n",
+  sprintf(line, "mm_compaction_finished: node=%d zone=%-8s order=%d ret=%s",
           event.nid(), MmCompactionSuitableArray[event.idx()], event.order(),
           MmCompactionRetArray[event.ret()]);
   return std::string(line);
@@ -835,7 +846,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_isolate_freepages: range=(0x%llx ~ 0x%llx) "
-          "nr_scanned=%llu nr_taken=%llu\\n",
+          "nr_scanned=%llu nr_taken=%llu",
           event.start_pfn(), event.end_pfn(), event.nr_scanned(),
           event.nr_taken());
   return std::string(line);
@@ -846,7 +857,7 @@
   char line[2048];
   sprintf(line,
           "mm_compaction_isolate_migratepages: range=(0x%llx ~ 0x%llx) "
-          "nr_scanned=%llu nr_taken=%llu\\n",
+          "nr_scanned=%llu nr_taken=%llu",
           event.start_pfn(), event.end_pfn(), event.nr_scanned(),
           event.nr_taken());
   return std::string(line);
@@ -855,7 +866,7 @@
 std::string FormatMmCompactionKcompactdSleep(
     const MmCompactionKcompactdSleepFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_compaction_kcompactd_sleep: nid=%d\\n", event.nid());
+  sprintf(line, "mm_compaction_kcompactd_sleep: nid=%d", event.nid());
   return std::string(line);
 }
 
@@ -863,7 +874,7 @@
     const MmCompactionKcompactdWakeFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "mm_compaction_kcompactd_wake: nid=%d order=%d classzone_idx=%-8s\\n",
+          "mm_compaction_kcompactd_wake: nid=%d order=%d classzone_idx=%-8s",
           event.nid(), event.order(),
           MmCompactionSuitableArray[event.classzone_idx()]);
   return std::string(line);
@@ -872,8 +883,7 @@
 std::string FormatMmCompactionMigratepages(
     const MmCompactionMigratepagesFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "mm_compaction_migratepages: nr_migrated=%llu nr_failed=%llu\\n",
+  sprintf(line, "mm_compaction_migratepages: nr_migrated=%llu nr_failed=%llu",
           event.nr_migrated(), event.nr_failed());
   return std::string(line);
 }
@@ -881,7 +891,7 @@
 std::string FormatMmCompactionSuitable(
     const MmCompactionSuitableFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "mm_compaction_suitable: node=%d zone=%-8s order=%d ret=%s\\n",
+  sprintf(line, "mm_compaction_suitable: node=%d zone=%-8s order=%d ret=%s",
           event.nid(), MmCompactionSuitableArray[event.idx()], event.order(),
           MmCompactionRetArray[event.ret()]);
   return std::string(line);
@@ -890,48 +900,46 @@
 std::string FormatMmCompactionTryToCompactPages(
     const MmCompactionTryToCompactPagesFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "mm_compaction_try_to_compact_pages: order=%d gfp_mask=0x%x mode=%d\\n",
-      event.order(), event.gfp_mask(),
-      event.mode());  // convert to int?
+  sprintf(line,
+          "mm_compaction_try_to_compact_pages: order=%d gfp_mask=0x%x mode=%d",
+          event.order(), event.gfp_mask(),
+          event.mode());  // convert to int?
   return std::string(line);
 }
 
 std::string FormatMmCompactionWakeupKcompactd(
     const MmCompactionWakeupKcompactdFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "mm_compaction_wakeup_kcompactd: nid=%d order=%d classzone_idx=%-8s\\n",
-      event.nid(), event.order(),
-      MmCompactionSuitableArray[event.classzone_idx()]);
+  sprintf(line,
+          "mm_compaction_wakeup_kcompactd: nid=%d order=%d classzone_idx=%-8s",
+          event.nid(), event.order(),
+          MmCompactionSuitableArray[event.classzone_idx()]);
   return std::string(line);
 }
 
 std::string FormatSuspendResume(const SuspendResumeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "suspend_resume: %s[%u] %s\\n", event.action().c_str(),
+  sprintf(line, "suspend_resume: %s[%u] %s", event.action().c_str(),
           event.val(), event.start() ? "begin" : "end");
   return std::string(line);
 }
 
 std::string FormatSchedWakeupNew(const SchedWakeupNewFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_wakeup_new: comm=%s pid=%d prio=%d target_cpu=%03d\\n",
+  sprintf(line, "sched_wakeup_new: comm=%s pid=%d prio=%d target_cpu=%03d",
           event.comm().c_str(), event.pid(), event.prio(), event.target_cpu());
   return std::string(line);
 }
 
 std::string FormatSchedProcessExec(const SchedProcessExecFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_process_exec: filename=%s pid=%d old_pid=%d\\n",
+  sprintf(line, "sched_process_exec: filename=%s pid=%d old_pid=%d",
           event.filename().c_str(), event.pid(), event.old_pid());
   return std::string(line);
 }
 std::string FormatSchedProcessExit(const SchedProcessExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_process_exit: comm=%s pid=%d tgid=%d prio=%d\\n",
+  sprintf(line, "sched_process_exit: comm=%s pid=%d tgid=%d prio=%d",
           event.comm().c_str(), event.pid(), event.tgid(), event.prio());
   return std::string(line);
 }
@@ -939,27 +947,27 @@
   char line[2048];
   sprintf(line,
           "sched_process_fork: parent_comm=%s parent_pid=%d child_comm=%s "
-          "child_pid=%d\\n",
+          "child_pid=%d",
           event.parent_comm().c_str(), event.parent_pid(),
           event.child_comm().c_str(), event.child_pid());
   return std::string(line);
 }
 std::string FormatSchedProcessFree(const SchedProcessFreeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_process_free: comm=%s pid=%d prio=%d\\n",
+  sprintf(line, "sched_process_free: comm=%s pid=%d prio=%d",
           event.comm().c_str(), event.pid(), event.prio());
   return std::string(line);
 }
 std::string FormatSchedProcessHang(const SchedProcessHangFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_process_hang: comm=%s pid=%d\\n", event.comm().c_str(),
+  sprintf(line, "sched_process_hang: comm=%s pid=%d", event.comm().c_str(),
           event.pid());
   return std::string(line);
 }
 
 std::string FormatSchedProcessWait(const SchedProcessWaitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "sched_process_wait: comm=%s pid=%d\\n", event.comm().c_str(),
+  sprintf(line, "sched_process_wait: comm=%s pid=%d", event.comm().c_str(),
           event.pid());
   return std::string(line);
 }
@@ -967,7 +975,7 @@
 std::string FormatTaskNewtask(const TaskNewtaskFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "task_newtask: comm=%s pid=%d clone_flags=%llu oom_score_adj=%d\\n",
+          "task_newtask: comm=%s pid=%d clone_flags=%llu oom_score_adj=%d",
           event.comm().c_str(), event.pid(), event.clone_flags(),
           event.oom_score_adj());
   return std::string(line);
@@ -975,7 +983,7 @@
 
 std::string FormatTaskRename(const TaskRenameFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "task_rename: pid=%d oldcomm=%s newcomm=%s oom_score_adj=%d\\n",
+  sprintf(line, "task_rename: pid=%d oldcomm=%s newcomm=%s oom_score_adj=%d",
           event.pid(), event.newcomm().c_str(), event.oldcomm().c_str(),
           event.oom_score_adj());
   return std::string(line);
@@ -983,9 +991,9 @@
 
 std::string FormatBlockBioBackmerge(const BlockBioBackmergeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_bio_backmerge: %d,%d %s %llu + %u [%s]\\n",
+  sprintf(line, "block_bio_backmerge: %d,%d %s %llu + %u [%s]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
@@ -994,45 +1002,46 @@
   char line[2048];
   sprintf(line,
           "block_bio_bounce:"
-          "%d,%d %s %llu + %u [%s]\\n",
+          "%d,%d %s %llu + %u [%s]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockBioComplete(const BlockBioCompleteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_bio_complete: %d,%d %s %llu + %u [%d]\\n",
+  sprintf(line, "block_bio_complete: %d,%d %s %llu + %u [%d]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(), event.error());
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
+          event.error());
   return std::string(line);
 }
 
 std::string FormatBlockBioFrontmerge(
     const BlockBioFrontmergeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_bio_frontmerge: %d,%d %s %llu + %u [%s]\\n",
+  sprintf(line, "block_bio_frontmerge: %d,%d %s %llu + %u [%s]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockBioQueue(const BlockBioQueueFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_bio_queue: %d,%d %s %llu + %u [%s]\\n",
-          major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+  sprintf(line, "block_bio_queue: %d,%d %s %llu + %u [%s]", major(event.dev()),
+          minor(event.dev()), event.rwbs().c_str(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockBioRemap(const BlockBioRemapFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_bio_remap:  %d,%d %s %llu + %u <- (%d,%d) %llu\\n",
+  sprintf(line, "block_bio_remap:  %d,%d %s %llu + %u <- (%d,%d) %llu",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           major(event.dev()), minor(event.dev()),
           (unsigned long long)event.old_sector());
   return std::string(line);
@@ -1040,60 +1049,61 @@
 
 std::string FormatBlockDirtyBuffer(const BlockDirtyBufferFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_dirty_buffer: %d,%d sector=%llu size=%zu\\n",
+  sprintf(line, "block_dirty_buffer: %d,%d sector=%llu size=%zu",
           major(event.dev()), minor(event.dev()),
-          (unsigned long long)event.sector(), (unsigned long)event.size());
+          static_cast<unsigned long long>(event.sector()),
+          static_cast<size_t>(event.size()));
   return std::string(line);
 }
 
 std::string FormatBlockGetrq(const BlockGetrqFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_getrq: %d,%d %s %llu + %u [%s]\\n", major(event.dev()),
+  sprintf(line, "block_getrq: %d,%d %s %llu + %u [%s]", major(event.dev()),
           minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockPlug(const BlockPlugFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_plug: comm=[%s]\\n", event.comm().c_str());
+  sprintf(line, "block_plug: comm=[%s]", event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockRqAbort(const BlockRqAbortFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_abort: %d,%d %s (%s) %llu + %u [%d]\\n",
+  sprintf(line, "block_rq_abort: %d,%d %s (%s) %llu + %u [%d]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          event.cmd().c_str(), (unsigned long long)event.sector(),
+          event.cmd().c_str(), static_cast<unsigned long long>(event.sector()),
           event.nr_sector(), event.errors());
   return std::string(line);
 }
 
 std::string FormatBlockRqComplete(const BlockRqCompleteFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_complete: %d,%d %s (%s) %llu + %u [%d]\\n",
+  sprintf(line, "block_rq_complete: %d,%d %s (%s) %llu + %u [%d]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          event.cmd().c_str(), (unsigned long long)event.sector(),
+          event.cmd().c_str(), static_cast<unsigned long long>(event.sector()),
           event.nr_sector(), event.errors());
   return std::string(line);
 }
 
 std::string FormatBlockRqInsert(const BlockRqInsertFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_insert: %d,%d %s %u (%s) %llu + %u [%s]\\n",
+  sprintf(line, "block_rq_insert: %d,%d %s %u (%s) %llu + %u [%s]",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
           event.bytes(), event.cmd().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockRqRemap(const BlockRqRemapFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_remap: %d,%d %s %llu + %u <- (%d,%d) %llu %u\\n",
+  sprintf(line, "block_rq_remap: %d,%d %s %llu + %u <- (%d,%d) %llu %u",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           major(event.dev()), minor(event.dev()),
           (unsigned long long)event.old_sector(), event.nr_bios());
   return std::string(line);
@@ -1101,43 +1111,43 @@
 
 std::string FormatBlockRqRequeue(const BlockRqRequeueFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_rq_requeue: %d,%d %s (%s) %llu + %u [%d\\n",
+  sprintf(line, "block_rq_requeue: %d,%d %s (%s) %llu + %u [%d",
           major(event.dev()), minor(event.dev()), event.rwbs().c_str(),
-          event.cmd().c_str(), (unsigned long long)event.sector(),
+          event.cmd().c_str(), static_cast<unsigned long long>(event.sector()),
           event.nr_sector(), event.errors());
   return std::string(line);
 }
 
 std::string FormatBlockSleeprq(const BlockSleeprqFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_sleeprq: %d,%d %s %llu + %u [%s]\\n", major(event.dev()),
+  sprintf(line, "block_sleeprq: %d,%d %s %llu + %u [%s]", major(event.dev()),
           minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(), event.nr_sector(),
+          static_cast<unsigned long long>(event.sector()), event.nr_sector(),
           event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockSplit(const BlockSplitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_split: %d,%d %s %llu / %llu [%s]\\n", major(event.dev()),
+  sprintf(line, "block_split: %d,%d %s %llu / %llu [%s]", major(event.dev()),
           minor(event.dev()), event.rwbs().c_str(),
-          (unsigned long long)event.sector(),
+          static_cast<unsigned long long>(event.sector()),
           (unsigned long long)event.new_sector(), event.comm().c_str());
   return std::string(line);
 }
 
 std::string FormatBlockTouchBuffer(const BlockTouchBufferFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_touch_buffer: %d,%d sector=%llu size=%zu\\n",
+  sprintf(line, "block_touch_buffer: %d,%d sector=%llu size=%zu",
           major(event.dev()), minor(event.dev()),
-          (unsigned long long)event.sector(), (unsigned long)event.size());
+          static_cast<unsigned long long>(event.sector()),
+          static_cast<size_t>(event.size()));
   return std::string(line);
 }
 
 std::string FormatBlockUnplug(const BlockUnplugFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "block_unplug: [%s] %d\\n", event.comm().c_str(),
-          event.nr_rq());
+  sprintf(line, "block_unplug: [%s] %d", event.comm().c_str(), event.nr_rq());
   return std::string(line);
 }
 
@@ -1145,7 +1155,7 @@
   char line[2048];
   sprintf(line,
           "ext4_alloc_da_blocks: dev %d,%d ino %lu data_blocks %u meta_blocks "
-          "%u \\n",
+          "%u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.data_blocks(), event.meta_blocks());
   return std::string(line);
@@ -1156,7 +1166,7 @@
   char line[2048];
   sprintf(line,
           "ext4_allocate_blocks: dev %d,%d ino %lu flags %s len %u block %llu "
-          "lblk %u goal %llu lleft %u lright %u pleft %llu pright %llu\\n",
+          "lblk %u goal %llu lleft %u lright %u pleft %llu pright %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           GetExt4HintFlag(event.flags()), event.len(), event.block(),
           event.logical(), event.goal(), event.lleft(), event.lright(),
@@ -1166,7 +1176,7 @@
 
 std::string FormatExt4AllocateInode(const Ext4AllocateInodeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_allocate_inode: dev %d,%d ino %lu dir %lu mode 0%o\\n",
+  sprintf(line, "ext4_allocate_inode: dev %d,%d ino %lu dir %lu mode 0%o",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.dir(), event.mode());
   return std::string(line);
@@ -1175,8 +1185,7 @@
 std::string FormatExt4BeginOrderedTruncate(
     const Ext4BeginOrderedTruncateFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "ext4_begin_ordered_truncate: dev %d,%d ino %lu new_size %lld\\n",
+  sprintf(line, "ext4_begin_ordered_truncate: dev %d,%d ino %lu new_size %lld",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.new_size());
   return std::string(line);
@@ -1184,8 +1193,7 @@
 
 std::string FormatExt4CollapseRange(const Ext4CollapseRangeFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "ext4_collapse_range: dev %d,%d ino %lu offset %lld len %lld\\n",
+  sprintf(line, "ext4_collapse_range: dev %d,%d ino %lu offset %lld len %lld",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.offset(), event.len());
   return std::string(line);
@@ -1197,7 +1205,7 @@
   sprintf(line,
           "ext4_da_release_space: dev %d,%d ino %lu mode 0%o i_blocks %llu "
           "freed_blocks %d reserved_data_blocks %d reserved_meta_blocks %d "
-          "allocated_meta_blocks %d\\n",
+          "allocated_meta_blocks %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.mode(), event.i_blocks(), event.freed_blocks(),
           event.reserved_data_blocks(), event.reserved_meta_blocks(),
@@ -1210,7 +1218,7 @@
   char line[2048];
   sprintf(line,
           "ext4_da_reserve_space:dev %d,%d ino %lu mode 0%o i_blocks %llu "
-          "reserved_data_blocks %d reserved_meta_blocks %d \\n",
+          "reserved_data_blocks %d reserved_meta_blocks %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.mode(), event.i_blocks(), event.reserved_data_blocks(),
           event.reserved_meta_blocks());
@@ -1223,7 +1231,7 @@
   sprintf(line,
           "ext4_da_update_reserve_space: dev %d,%d ino %lu mode 0%o i_blocks "
           "%llu used_blocks %d reserved_data_blocks %d reserved_meta_blocks %d "
-          "allocated_meta_blocks %d quota_claim %d\\n",
+          "allocated_meta_blocks %d quota_claim %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.mode(), event.i_blocks(), event.used_blocks(),
           event.reserved_data_blocks(), event.reserved_meta_blocks(),
@@ -1235,7 +1243,7 @@
   char line[2048];
   sprintf(line,
           "ext4_da_write_pages: dev %d,%d ino %lu first_page %lu nr_to_write "
-          "%ld sync_mode %d\\n",
+          "%ld sync_mode %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.first_page(), (long)event.nr_to_write(),
           event.sync_mode());
@@ -1247,7 +1255,7 @@
     const Ext4DaWritePagesExtentFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_da_write_pages_extent: dev %d,%d ino %lu lblk %llu len %u \\n",
+          "ext4_da_write_pages_extent: dev %d,%d ino %lu lblk %llu len %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.len());
   return std::string(line);
@@ -1255,7 +1263,7 @@
 
 std::string FormatExt4DiscardBlocks(const Ext4DiscardBlocksFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_discard_blocks: dev %d,%d blk %llu count %llu\\n",
+  sprintf(line, "ext4_discard_blocks: dev %d,%d blk %llu count %llu",
           major(event.dev()), minor(event.dev()), event.blk(), event.count());
   return std::string(line);
 }
@@ -1263,14 +1271,14 @@
 std::string FormatExt4DiscardPreallocations(
     const Ext4DiscardPreallocationsFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_discard_preallocations: dev %d,%d ino %lu\\n",
+  sprintf(line, "ext4_discard_preallocations: dev %d,%d ino %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino());
   return std::string(line);
 }
 
 std::string FormatExt4DropInode(const Ext4DropInodeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_drop_inode: dev %d,%d ino %lu drop %d\\n",
+  sprintf(line, "ext4_drop_inode: dev %d,%d ino %lu drop %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.drop());
   return std::string(line);
@@ -1280,7 +1288,7 @@
 std::string FormatExt4EsCacheExtent(const Ext4EsCacheExtentFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_es_cache_extent: dev %d,%d ino %lu es [%u/%u) mapped %llu \\n",
+          "ext4_es_cache_extent: dev %d,%d ino %lu es [%u/%u) mapped %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.len(), event.pblk());
   return std::string(line);
@@ -1289,11 +1297,10 @@
 std::string FormatExt4EsFindDelayedExtentRangeEnter(
     const Ext4EsFindDelayedExtentRangeEnterFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_es_find_delayed_extent_range_enter: dev %d,%d ino %lu lblk %u\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      event.lblk());
+  sprintf(line,
+          "ext4_es_find_delayed_extent_range_enter: dev %d,%d ino %lu lblk %u",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          event.lblk());
   return std::string(line);
 }
 
@@ -1303,7 +1310,7 @@
   char line[2048];
   sprintf(line,
           "ext4_es_find_delayed_extent_range_exit: dev %d,%d ino %lu es "
-          "[%u/%u) mapped %llu\\n",
+          "[%u/%u) mapped %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.len(), event.pblk());
   return std::string(line);
@@ -1314,7 +1321,7 @@
     const Ext4EsInsertExtentFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_es_insert_extent: dev %d,%d ino %lu es [%u/%u) mapped %llu \\n",
+          "ext4_es_insert_extent: dev %d,%d ino %lu es [%u/%u) mapped %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.len(), event.pblk());
   return std::string(line);
@@ -1323,7 +1330,7 @@
 std::string FormatExt4EsLookupExtentEnter(
     const Ext4EsLookupExtentEnterFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_es_lookup_extent_enter: dev %d,%d ino %lu lblk %u\\n",
+  sprintf(line, "ext4_es_lookup_extent_enter: dev %d,%d ino %lu lblk %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk());
   return std::string(line);
@@ -1333,19 +1340,18 @@
 std::string FormatExt4EsLookupExtentExit(
     const Ext4EsLookupExtentExitFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_es_lookup_extent_exit: dev %d,%d ino %lu found %d [%u/%u) %llu\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      event.found(), event.lblk(), event.len(),
-      event.found() ? event.pblk() : 0);
+  sprintf(line,
+          "ext4_es_lookup_extent_exit: dev %d,%d ino %lu found %d [%u/%u) %llu",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          event.found(), event.lblk(), event.len(),
+          event.found() ? event.pblk() : 0);
   return std::string(line);
 }
 
 std::string FormatExt4EsRemoveExtent(
     const Ext4EsRemoveExtentFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_es_remove_extent: dev %d,%d ino %lu es [%lld/%lld)\\n",
+  sprintf(line, "ext4_es_remove_extent: dev %d,%d ino %lu es [%lld/%lld)",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.len());
   return std::string(line);
@@ -1355,7 +1361,7 @@
   char line[2048];
   sprintf(line,
           "ext4_es_shrink: dev %d,%d nr_shrunk %d, scan_time %llu nr_skipped "
-          "%d retried %d\\n",
+          "%d retried %d",
           major(event.dev()), minor(event.dev()), event.nr_shrunk(),
           event.scan_time(), event.nr_skipped(), event.retried());
   return std::string(line);
@@ -1363,7 +1369,7 @@
 
 std::string FormatExt4EsShrinkCount(const Ext4EsShrinkCountFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_es_shrink_count: dev %d,%d nr_to_scan %d cache_cnt %d\\n",
+  sprintf(line, "ext4_es_shrink_count: dev %d,%d nr_to_scan %d cache_cnt %d",
           major(event.dev()), minor(event.dev()), event.nr_to_scan(),
           event.cache_cnt());
   return std::string(line);
@@ -1373,7 +1379,7 @@
     const Ext4EsShrinkScanEnterFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_es_shrink_scan_enter: dev %d,%d nr_to_scan %d cache_cnt %d\\n",
+          "ext4_es_shrink_scan_enter: dev %d,%d nr_to_scan %d cache_cnt %d",
           major(event.dev()), minor(event.dev()), event.nr_to_scan(),
           event.cache_cnt());
   return std::string(line);
@@ -1382,8 +1388,7 @@
 std::string FormatExt4EsShrinkScanExit(
     const Ext4EsShrinkScanExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "ext4_es_shrink_scan_exit: dev %d,%d nr_shrunk %d cache_cnt %d\\n",
+  sprintf(line, "ext4_es_shrink_scan_exit: dev %d,%d nr_shrunk %d cache_cnt %d",
           major(event.dev()), minor(event.dev()), event.nr_shrunk(),
           event.cache_cnt());
   return std::string(line);
@@ -1391,7 +1396,7 @@
 
 std::string FormatExt4EvictInode(const Ext4EvictInodeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_evict_inode: dev %d,%d ino %lu nlink %d\\n",
+  sprintf(line, "ext4_evict_inode: dev %d,%d ino %lu nlink %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.nlink());
   return std::string(line);
@@ -1402,7 +1407,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ext_convert_to_initialized_enter: dev %d,%d ino %lu m_lblk %u "
-          "m_len %u u_lblk %u u_len %u u_pblk %llu\\n",
+          "m_len %u u_lblk %u u_len %u u_pblk %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.m_lblk(), event.m_len(), event.u_lblk(), event.u_len(),
           event.u_pblk());
@@ -1415,7 +1420,7 @@
   sprintf(line,
           "ext4_ext_convert_to_initialized_fastpath: dev %d,%d ino %lu m_lblk "
           "%u m_len %u u_lblk %u u_len %u u_pblk %llu i_lblk %u i_len %u "
-          "i_pblk %llu\\n",
+          "i_pblk %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.m_lblk(), event.m_len(), event.u_lblk(), event.u_len(),
           event.u_pblk(), event.i_lblk(), event.i_len(), event.i_pblk());
@@ -1427,7 +1432,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ext_handle_unwritten_extents: dev %d,%d ino %lu m_lblk %u "
-          "m_pblk %llu m_len %u flags %s allocated %d newblock %llu\\n",
+          "m_pblk %llu m_len %u flags %s allocated %d newblock %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.lblk(), (unsigned long long)event.pblk(), event.len(),
           GetExt4ExtFlag(event.flags()), (unsigned int)event.allocated(),
@@ -1437,7 +1442,7 @@
 
 std::string FormatExt4ExtInCache(const Ext4ExtInCacheFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_ext_in_cache: dev %d,%d ino %lu lblk %u ret %d\\n",
+  sprintf(line, "ext4_ext_in_cache: dev %d,%d ino %lu lblk %u ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.lblk(), event.ret());
   return std::string(line);
@@ -1445,7 +1450,7 @@
 
 std::string FormatExt4ExtLoadExtent(const Ext4ExtLoadExtentFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_ext_load_extent: dev %d,%d ino %lu lblk %u pblk %llu\\n",
+  sprintf(line, "ext4_ext_load_extent: dev %d,%d ino %lu lblk %u pblk %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.pblk());
   return std::string(line);
@@ -1456,7 +1461,7 @@
   char line[2048];
   sprintf(
       line,
-      "ext4_ext_map_blocks_enter: dev %d,%d ino %lu lblk %u len %u flags %s\\n",
+      "ext4_ext_map_blocks_enter: dev %d,%d ino %lu lblk %u len %u flags %s",
       major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
       event.lblk(), event.len(), GetExt4ExtFlag(event.flags()));
   return std::string(line);
@@ -1467,7 +1472,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ext_map_blocks_exit: dev %d,%d ino %lu lblk %u pblk %llu len "
-          "%u flags %x ret %d\\n",
+          "%u flags %x ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.pblk(), event.len(), event.flags(), event.ret());
   return std::string(line);
@@ -1475,22 +1480,21 @@
 
 std::string FormatExt4ExtPutInCache(const Ext4ExtPutInCacheFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_ext_put_in_cache: dev %d,%d ino %lu lblk %u len %u start %llu\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      (unsigned)event.lblk(), event.len(), (unsigned long long)event.start());
+  sprintf(line,
+          "ext4_ext_put_in_cache: dev %d,%d ino %lu lblk %u len %u start %llu",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          (unsigned)event.lblk(), event.len(),
+          (unsigned long long)event.start());
   return std::string(line);
 }
 
 std::string FormatExt4ExtRemoveSpace(
     const Ext4ExtRemoveSpaceFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_ext_remove_space: dev %d,%d ino %lu since %u end %u depth %d\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      (unsigned)event.start(), (unsigned)event.end(), event.depth());
+  sprintf(line,
+          "ext4_ext_remove_space: dev %d,%d ino %lu since %u end %u depth %d",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          (unsigned)event.start(), (unsigned)event.end(), event.depth());
   return std::string(line);
 }
 
@@ -1499,7 +1503,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ext_remove_space_done: dev %d,%d ino %lu since %u end %u depth "
-          "%d partial %lld remaining_entries %u\\n",
+          "%d partial %lld remaining_entries %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.start(), (unsigned)event.end(), event.depth(),
           (long long)event.partial(), (unsigned short)event.eh_entries());
@@ -1508,7 +1512,7 @@
 
 std::string FormatExt4ExtRmIdx(const Ext4ExtRmIdxFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_ext_rm_idx: dev %d,%d ino %lu index_pblk %llu\\n",
+  sprintf(line, "ext4_ext_rm_idx: dev %d,%d ino %lu index_pblk %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long long)event.pblk());
   return std::string(line);
@@ -1518,7 +1522,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ext_rm_leaf: dev %d,%d ino %lu start_lblk %u last_extent "
-          "[%u(%llu), %u]partial_cluster %lld\\n",
+          "[%u(%llu), %u]partial_cluster %lld",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.start(), (unsigned)event.ee_lblk(),
           (unsigned long long)event.ee_pblk(), (unsigned short)event.ee_len(),
@@ -1529,7 +1533,7 @@
 std::string FormatExt4ExtShowExtent(const Ext4ExtShowExtentFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_ext_show_extent: dev %d,%d ino %lu lblk %u pblk %llu len %u\\n",
+          "ext4_ext_show_extent: dev %d,%d ino %lu lblk %u pblk %llu len %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.lblk(), (unsigned long long)event.pblk(),
           (unsigned short)event.len());
@@ -1541,7 +1545,7 @@
   char line[2048];
   sprintf(
       line,
-      "ext4_fallocate_enter: dev %d,%d ino %lu offset %lld len %lld mode %s\\n",
+      "ext4_fallocate_enter: dev %d,%d ino %lu offset %lld len %lld mode %s",
       major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
       event.offset(), event.len(), GetExt4ModeFlag(event.mode()));
   return std::string(line);
@@ -1550,7 +1554,7 @@
 std::string FormatExt4FallocateExit(const Ext4FallocateExitFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_fallocate_exit: dev %d,%d ino %lu pos %lld blocks %u ret %d\\n",
+          "ext4_fallocate_exit: dev %d,%d ino %lu pos %lld blocks %u ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.blocks(), event.ret());
   return std::string(line);
@@ -1561,7 +1565,7 @@
   char line[2048];
   sprintf(line,
           "ext4_find_delalloc_range: dev %d,%d ino %lu from %u to %u reverse "
-          "%d found %d (blk = %u)\\n",
+          "%d found %d (blk = %u)",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.from(), (unsigned)event.to(), event.reverse(),
           event.found(), (unsigned)event.found_blk());
@@ -1570,11 +1574,10 @@
 
 std::string FormatExt4Forget(const Ext4ForgetFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_forget: dev %d,%d ino %lu mode 0%o is_metadata %d block %llu\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      event.mode(), event.is_metadata(), event.block());
+  sprintf(line,
+          "ext4_forget: dev %d,%d ino %lu mode 0%o is_metadata %d block %llu",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          event.mode(), event.is_metadata(), event.block());
   return std::string(line);
 }
 
@@ -1582,7 +1585,7 @@
   char line[2048];
   sprintf(line,
           "ext4_free_blocks: dev %d,%d ino %lu mode 0%o block %llu count %lu "
-          "flags %s\\n",
+          "flags %s",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.mode(), event.block(), (unsigned long)event.count(),
           GetExt4FreeBlocksFlag(event.flags()));
@@ -1593,7 +1596,7 @@
   char line[2048];
   sprintf(line,
           "ext4_free_inode: dev %d,%d ino %lu mode 0%o uid %u gid %u blocks "
-          "%llu\\n",
+          "%llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.mode(), event.uid(), event.gid(), event.blocks());
   return std::string(line);
@@ -1604,7 +1607,7 @@
   char line[2048];
   sprintf(line,
           "ext4_get_implied_cluster_alloc_exit: dev %d,%d m_lblk %u m_pblk "
-          "%llu m_len %u m_flags %u ret %d\\n",
+          "%llu m_len %u m_flags %u ret %d",
           major(event.dev()), minor(event.dev()), event.lblk(),
           (unsigned long long)event.pblk(), event.len(), event.flags(),
           event.ret());
@@ -1614,11 +1617,10 @@
 std::string FormatExt4GetReservedClusterAlloc(
     const Ext4GetReservedClusterAllocFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_get_reserved_cluster_alloc: dev %d,%d ino %lu lblk %u len %u\\n",
-      major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
-      (unsigned)event.lblk(), event.len());
+  sprintf(line,
+          "ext4_get_reserved_cluster_alloc: dev %d,%d ino %lu lblk %u len %u",
+          major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
+          (unsigned)event.lblk(), event.len());
   return std::string(line);
 }
 
@@ -1627,7 +1629,7 @@
   char line[2048];
   sprintf(
       line,
-      "ext4_ind_map_blocks_enter: dev %d,%d ino %lu lblk %u len %u flags %u\\n",
+      "ext4_ind_map_blocks_enter: dev %d,%d ino %lu lblk %u len %u flags %u",
       major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
       event.lblk(), event.len(), event.flags());
   return std::string(line);
@@ -1638,7 +1640,7 @@
   char line[2048];
   sprintf(line,
           "ext4_ind_map_blocks_exit: dev %d,%d ino %lu lblk %u pblk %llu len "
-          "%u flags %x ret %d\\n",
+          "%u flags %x ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.lblk(), event.pblk(), event.len(), event.flags(), event.ret());
   return std::string(line);
@@ -1646,7 +1648,7 @@
 
 std::string FormatExt4InsertRange(const Ext4InsertRangeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_insert_range: dev %d,%d ino %lu offset %lld len %lld\\n",
+  sprintf(line, "ext4_insert_range: dev %d,%d ino %lu offset %lld len %lld",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.offset(), event.len());
   return std::string(line);
@@ -1657,7 +1659,7 @@
   char line[2048];
   sprintf(line,
           "ext4_invalidatepage: dev %d,%d ino %lu page_index %lu offset %u "
-          "length %u\\n",
+          "length %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.index(), event.offset(), event.length());
   return std::string(line);
@@ -1665,19 +1667,17 @@
 
 std::string FormatExt4JournalStart(const Ext4JournalStartFtraceEvent& event) {
   char line[2048];
-  sprintf(
-      line,
-      "ext4_journal_start: dev %d,%d blocks, %d rsv_blocks, %d caller %pS\\n",
-      major(event.dev()), minor(event.dev()), event.blocks(),
-      event.rsv_blocks(), (void*)event.ip());
+  sprintf(line,
+          "ext4_journal_start: dev %d,%d blocks, %d rsv_blocks, %d caller %pS",
+          major(event.dev()), minor(event.dev()), event.blocks(),
+          event.rsv_blocks(), (void*)event.ip());
   return std::string(line);
 }
 
 std::string FormatExt4JournalStartReserved(
     const Ext4JournalStartReservedFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "ext4_journal_start_reserved: dev %d,%d blocks, %d caller %pS\\n",
+  sprintf(line, "ext4_journal_start_reserved: dev %d,%d blocks, %d caller %pS",
           major(event.dev()), minor(event.dev()), event.blocks(),
           (void*)event.ip());
   return std::string(line);
@@ -1688,7 +1688,7 @@
   char line[2048];
   sprintf(line,
           "ext4_journalled_invalidatepage: dev %d,%d ino %lu page_index %lu "
-          "offset %u length %u\\n",
+          "offset %u length %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.index(), event.offset(), event.length());
   return std::string(line);
@@ -1699,7 +1699,7 @@
   char line[2048];
   sprintf(line,
           "ext4_journalled_write_end: dev %d,%d ino %lu pos %lld len %u copied "
-          "%u\\n",
+          "%u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.len(), event.copied());
   return std::string(line);
@@ -1707,7 +1707,7 @@
 
 std::string FormatExt4LoadInode(const Ext4LoadInodeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_load_inode: dev %d,%d ino %ld\\n", major(event.dev()),
+  sprintf(line, "ext4_load_inode: dev %d,%d ino %ld", major(event.dev()),
           minor(event.dev()), (unsigned long)event.ino());
   return std::string(line);
 }
@@ -1715,7 +1715,7 @@
 std::string FormatExt4LoadInodeBitmap(
     const Ext4LoadInodeBitmapFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_load_inode_bitmap: dev %d,%d group %u\\n",
+  sprintf(line, "ext4_load_inode_bitmap: dev %d,%d group %u",
           major(event.dev()), minor(event.dev()), event.group());
   return std::string(line);
 }
@@ -1723,7 +1723,7 @@
 std::string FormatExt4MarkInodeDirty(
     const Ext4MarkInodeDirtyFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mark_inode_dirty: dev %d,%d ino %lu caller %pS\\n",
+  sprintf(line, "ext4_mark_inode_dirty: dev %d,%d ino %lu caller %pS",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (void*)event.ip());
   return std::string(line);
@@ -1731,15 +1731,15 @@
 
 std::string FormatExt4MbBitmapLoad(const Ext4MbBitmapLoadFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mb_bitmap_load: dev %d,%d group %u\\n",
-          major(event.dev()), minor(event.dev()), event.group());
+  sprintf(line, "ext4_mb_bitmap_load: dev %d,%d group %u", major(event.dev()),
+          minor(event.dev()), event.group());
   return std::string(line);
 }
 
 std::string FormatExt4MbBuddyBitmapLoad(
     const Ext4MbBuddyBitmapLoadFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mb_buddy_bitmap_load: dev %d,%d group %u\\n",
+  sprintf(line, "ext4_mb_buddy_bitmap_load: dev %d,%d group %u",
           major(event.dev()), minor(event.dev()), event.group());
   return std::string(line);
 }
@@ -1747,7 +1747,7 @@
 std::string FormatExt4MbDiscardPreallocations(
     const Ext4MbDiscardPreallocationsFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mb_discard_preallocations: dev %d,%d needed %d\\n",
+  sprintf(line, "ext4_mb_discard_preallocations: dev %d,%d needed %d",
           major(event.dev()), minor(event.dev()), event.needed());
   return std::string(line);
 }
@@ -1756,7 +1756,7 @@
   char line[2048];
   sprintf(line,
           "ext4_mb_new_group_pa: dev %d,%d ino %lu pstart %llu len %u lstart "
-          "%llu\\n",
+          "%llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pa_pstart(), event.pa_len(), event.pa_lstart());
   return std::string(line);
@@ -1766,7 +1766,7 @@
   char line[2048];
   sprintf(line,
           "ext4_mb_new_inode_pa: dev %d,%d ino %lu pstart %llu len %u lstart "
-          "%llu\\n",
+          "%llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pa_pstart(), event.pa_len(), event.pa_lstart());
   return std::string(line);
@@ -1775,7 +1775,7 @@
 std::string FormatExt4MbReleaseGroupPa(
     const Ext4MbReleaseGroupPaFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mb_release_group_pa: dev %d,%d pstart %llu len %u\\n",
+  sprintf(line, "ext4_mb_release_group_pa: dev %d,%d pstart %llu len %u",
           major(event.dev()), minor(event.dev()), event.pa_pstart(),
           event.pa_len());
   return std::string(line);
@@ -1785,7 +1785,7 @@
     const Ext4MbReleaseInodePaFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_mb_release_inode_pa: dev %d,%d ino %lu block %llu count %u\\n",
+          "ext4_mb_release_inode_pa: dev %d,%d ino %lu block %llu count %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.block(), event.count());
   return std::string(line);
@@ -1796,7 +1796,7 @@
   sprintf(line,
           "ext4_mballoc_alloc: dev %d,%d inode %lu orig %u/%d/%u@%u goal "
           "%u/%d/%u@%u result %u/%d/%u@%u blks %u grps %u cr %u flags %s tail "
-          "%u broken %u\\n",
+          "%u broken %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.orig_group(), event.orig_start(), event.orig_len(),
           event.orig_logical(), event.goal_group(), event.goal_start(),
@@ -1811,7 +1811,7 @@
 std::string FormatExt4MballocDiscard(
     const Ext4MballocDiscardFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mballoc_discard: dev %d,%d inode %lu extent %u/%d/%d \\n",
+  sprintf(line, "ext4_mballoc_discard: dev %d,%d inode %lu extent %u/%d/%d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.result_group(), event.result_start(), event.result_len());
   return std::string(line);
@@ -1819,7 +1819,7 @@
 
 std::string FormatExt4MballocFree(const Ext4MballocFreeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_mballoc_free: dev %d,%d inode %lu extent %u/%d/%d \\n",
+  sprintf(line, "ext4_mballoc_free: dev %d,%d inode %lu extent %u/%d/%d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.result_group(), event.result_start(), event.result_len());
   return std::string(line);
@@ -1830,7 +1830,7 @@
   char line[2048];
   sprintf(line,
           "ext4_mballoc_prealloc: dev %d,%d inode %lu orig %u/%d/%u@%u result "
-          "%u/%d/%u@%u\\n",
+          "%u/%d/%u@%u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.orig_group(), event.orig_start(), event.orig_len(),
           event.orig_logical(), event.result_group(), event.result_start(),
@@ -1843,7 +1843,7 @@
   char line[2048];
   sprintf(line,
           "ext4_other_inode_update_time: dev %d,%d orig_ino %lu ino %lu mode "
-          "0%o uid %u gid %u\\n",
+          "0%o uid %u gid %u",
           major(event.dev()), minor(event.dev()),
           (unsigned long)event.orig_ino(), (unsigned long)event.ino(),
           event.mode(), event.uid(), event.gid());
@@ -1853,7 +1853,7 @@
 std::string FormatExt4PunchHole(const Ext4PunchHoleFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_punch_hole: dev %d,%d ino %lu offset %lld len %lld mode %s\\n",
+          "ext4_punch_hole: dev %d,%d ino %lu offset %lld len %lld mode %s",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.offset(), event.len(), GetExt4ModeFlag(event.mode()));
   return std::string(line);
@@ -1862,14 +1862,14 @@
 std::string FormatExt4ReadBlockBitmapLoad(
     const Ext4ReadBlockBitmapLoadFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_read_block_bitmap_load: dev %d,%d group %u\\n",
+  sprintf(line, "ext4_read_block_bitmap_load: dev %d,%d group %u",
           major(event.dev()), minor(event.dev()), event.group());
   return std::string(line);
 }
 
 std::string FormatExt4Readpage(const Ext4ReadpageFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_readpage: dev %d,%d ino %lu page_index %lu\\n",
+  sprintf(line, "ext4_readpage: dev %d,%d ino %lu page_index %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.index());
   return std::string(line);
@@ -1877,7 +1877,7 @@
 
 std::string FormatExt4Releasepage(const Ext4ReleasepageFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_releasepage: dev %d,%d ino %lu page_index %lu\\n",
+  sprintf(line, "ext4_releasepage: dev %d,%d ino %lu page_index %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.index());
   return std::string(line);
@@ -1887,7 +1887,7 @@
   char line[2048];
   sprintf(line,
           "ext4_remove_blocks: dev %d,%d ino %lu extent [%u(%llu), %u]from %u "
-          "to %u partial_cluster %lld\\n",
+          "to %u partial_cluster %lld",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned)event.ee_lblk(), (unsigned long long)event.ee_pblk(),
           (unsigned short)event.ee_len(), (unsigned)event.from(),
@@ -1899,7 +1899,7 @@
   char line[2048];
   sprintf(line,
           "ext4_request_blocks: dev %d,%d ino %lu flags %s len %u lblk %u goal "
-          "%llu lleft %u lright %u pleft %llu pright %llu \\n",
+          "%llu lleft %u lright %u pleft %llu pright %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           GetExt4HintFlag(event.flags()), event.len(), event.logical(),
           event.goal(), event.lleft(), event.lright(), event.pleft(),
@@ -1909,7 +1909,7 @@
 
 std::string FormatExt4RequestInode(const Ext4RequestInodeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_request_inode: dev %d,%d dir %lu mode 0%o\\n",
+  sprintf(line, "ext4_request_inode: dev %d,%d dir %lu mode 0%o",
           major(event.dev()), minor(event.dev()), (unsigned long)event.dir(),
           event.mode());
   return std::string(line);
@@ -1917,14 +1917,14 @@
 
 std::string FormatExt4SyncFs(const Ext4SyncFsFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_sync_fs: dev %d,%d wait %d\\n", major(event.dev()),
+  sprintf(line, "ext4_sync_fs: dev %d,%d wait %d", major(event.dev()),
           minor(event.dev()), event.wait());
   return std::string(line);
 }
 
 std::string FormatExt4TrimAllFree(const Ext4TrimAllFreeFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_trim_all_free: dev %d,%d group %u, start %d, len %d\\n",
+  sprintf(line, "ext4_trim_all_free: dev %d,%d group %u, start %d, len %d",
           event.dev_major(), event.dev_minor(), event.group(), event.start(),
           event.len());
   return std::string(line);
@@ -1932,7 +1932,7 @@
 
 std::string FormatExt4TrimExtent(const Ext4TrimExtentFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_trim_extent: dev %d,%d group %u, start %d, len %d\\n",
+  sprintf(line, "ext4_trim_extent: dev %d,%d group %u, start %d, len %d",
           event.dev_major(), event.dev_minor(), event.group(), event.start(),
           event.len());
   return std::string(line);
@@ -1940,7 +1940,7 @@
 
 std::string FormatExt4TruncateEnter(const Ext4TruncateEnterFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_truncate_enter: dev %d,%d ino %lu blocks %llu\\n",
+  sprintf(line, "ext4_truncate_enter: dev %d,%d ino %lu blocks %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.blocks());
   return std::string(line);
@@ -1948,7 +1948,7 @@
 
 std::string FormatExt4TruncateExit(const Ext4TruncateExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_truncate_exit: dev %d,%d ino %lu blocks %llu\\n",
+  sprintf(line, "ext4_truncate_exit: dev %d,%d ino %lu blocks %llu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.blocks());
   return std::string(line);
@@ -1956,7 +1956,7 @@
 
 std::string FormatExt4UnlinkEnter(const Ext4UnlinkEnterFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_unlink_enter: dev %d,%d ino %lu size %lld parent %lu\\n",
+  sprintf(line, "ext4_unlink_enter: dev %d,%d ino %lu size %lld parent %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.size(), (unsigned long)event.parent());
   return std::string(line);
@@ -1964,7 +1964,7 @@
 
 std::string FormatExt4UnlinkExit(const Ext4UnlinkExitFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_unlink_exit: dev %d,%d ino %lu ret %d\\n",
+  sprintf(line, "ext4_unlink_exit: dev %d,%d ino %lu ret %d",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.ret());
   return std::string(line);
@@ -1972,8 +1972,7 @@
 
 std::string FormatExt4WriteBegin(const Ext4WriteBeginFtraceEvent& event) {
   char line[2048];
-  sprintf(line,
-          "ext4_write_begin: dev %d,%d ino %lu pos %lld len %u flags %u\\n",
+  sprintf(line, "ext4_write_begin: dev %d,%d ino %lu pos %lld len %u flags %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.len(), event.flags());
   return std::string(line);
@@ -1981,7 +1980,7 @@
 
 std::string FormatExt4WriteEnd(const Ext4WriteEndFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_write_end: %d,%d ino %lu pos %lld len %u copied %u\\n",
+  sprintf(line, "ext4_write_end: %d,%d ino %lu pos %lld len %u copied %u",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.pos(), event.len(), event.copied());
   return std::string(line);
@@ -1989,7 +1988,7 @@
 
 std::string FormatExt4Writepage(const Ext4WritepageFtraceEvent& event) {
   char line[2048];
-  sprintf(line, "ext4_writepage: dev %d,%d ino %lu page_index %lu\\n",
+  sprintf(line, "ext4_writepage: dev %d,%d ino %lu page_index %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (unsigned long)event.index());
   return std::string(line);
@@ -2000,7 +1999,7 @@
   sprintf(line,
           "ext4_writepages: dev %d,%d ino %lu nr_to_write %ld pages_skipped "
           "%ld range_start %lld range_end %lld sync_mode %d for_kupdate %d "
-          "range_cyclic %d writeback_index %lu\\n",
+          "range_cyclic %d writeback_index %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           (long)event.nr_to_write(), (long)event.pages_skipped(),
           event.range_start(), event.range_end(), event.sync_mode(),
@@ -2014,7 +2013,7 @@
   char line[2048];
   sprintf(line,
           "ext4_writepages_result: dev %d,%d ino %lu ret %d pages_written %d "
-          "pages_skipped %ld sync_mode %d writeback_index %lu \\n",
+          "pages_skipped %ld sync_mode %d writeback_index %lu",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.ret(), event.pages_written(), (long)event.pages_skipped(),
           event.sync_mode(), (unsigned long)event.writeback_index());
@@ -2024,7 +2023,7 @@
 std::string FormatExt4ZeroRange(const Ext4ZeroRangeFtraceEvent& event) {
   char line[2048];
   sprintf(line,
-          "ext4_zero_range: dev %d,%d ino %lu offset %lld len %lld mode %s\\n ",
+          "ext4_zero_range: dev %d,%d ino %lu offset %lld len %lld mode %s",
           major(event.dev()), minor(event.dev()), (unsigned long)event.ino(),
           event.offset(), event.len(), GetExt4ModeFlag(event.mode()));
   return std::string(line);
diff --git a/tools/trace_to_text/ftrace_inode_handler.cc b/tools/trace_to_text/ftrace_inode_handler.cc
new file mode 100644
index 0000000..ad0d9b2
--- /dev/null
+++ b/tools/trace_to_text/ftrace_inode_handler.cc
@@ -0,0 +1,333 @@
+/*
+ * 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 "tools/trace_to_text/ftrace_inode_handler.h"
+
+namespace perfetto {
+
+bool ParseInode(const protos::FtraceEvent& event, uint64_t* inode) {
+  if (event.has_ext4_alloc_da_blocks() && event.ext4_alloc_da_blocks().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_alloc_da_blocks().ino());
+    return true;
+  } else if (event.has_ext4_allocate_blocks() &&
+             event.ext4_allocate_blocks().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_allocate_blocks().ino());
+    return true;
+  } else if (event.has_ext4_allocate_inode() &&
+             event.ext4_allocate_inode().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_allocate_inode().ino());
+    return true;
+  } else if (event.has_ext4_begin_ordered_truncate() &&
+             event.ext4_begin_ordered_truncate().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_begin_ordered_truncate().ino());
+    return true;
+  } else if (event.has_ext4_collapse_range() &&
+             event.ext4_collapse_range().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_collapse_range().ino());
+    return true;
+  } else if (event.has_ext4_da_release_space() &&
+             event.ext4_da_release_space().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_release_space().ino());
+    return true;
+  } else if (event.has_ext4_da_reserve_space() &&
+             event.ext4_da_reserve_space().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_reserve_space().ino());
+    return true;
+  } else if (event.has_ext4_da_update_reserve_space() &&
+             event.ext4_da_update_reserve_space().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_update_reserve_space().ino());
+    return true;
+  } else if (event.has_ext4_da_write_begin() &&
+             event.ext4_da_write_begin().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_write_begin().ino());
+    return true;
+  } else if (event.has_ext4_da_write_end() && event.ext4_da_write_end().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_write_end().ino());
+    return true;
+  } else if (event.has_ext4_da_write_pages() &&
+             event.ext4_da_write_pages().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_write_pages().ino());
+    return true;
+  } else if (event.has_ext4_da_write_pages_extent() &&
+             event.ext4_da_write_pages_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_da_write_pages_extent().ino());
+    return true;
+  } else if (event.has_ext4_direct_io_enter() &&
+             event.ext4_direct_io_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_direct_io_enter().ino());
+    return true;
+  } else if (event.has_ext4_direct_io_exit() &&
+             event.ext4_direct_io_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_direct_io_exit().ino());
+    return true;
+  } else if (event.has_ext4_discard_preallocations() &&
+             event.ext4_discard_preallocations().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_discard_preallocations().ino());
+    return true;
+  } else if (event.has_ext4_drop_inode() && event.ext4_drop_inode().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_drop_inode().ino());
+    return true;
+  } else if (event.has_ext4_es_cache_extent() &&
+             event.ext4_es_cache_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_es_cache_extent().ino());
+    return true;
+  } else if (event.has_ext4_es_find_delayed_extent_range_enter() &&
+             event.ext4_es_find_delayed_extent_range_enter().ino()) {
+    *inode = static_cast<uint64_t>(
+        event.ext4_es_find_delayed_extent_range_enter().ino());
+    return true;
+  } else if (event.has_ext4_es_find_delayed_extent_range_exit() &&
+             event.ext4_es_find_delayed_extent_range_exit().ino()) {
+    *inode = static_cast<uint64_t>(
+        event.ext4_es_find_delayed_extent_range_exit().ino());
+    return true;
+  } else if (event.has_ext4_es_insert_extent() &&
+             event.ext4_es_insert_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_es_insert_extent().ino());
+    return true;
+  } else if (event.has_ext4_es_lookup_extent_enter() &&
+             event.ext4_es_lookup_extent_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_es_lookup_extent_enter().ino());
+    return true;
+  } else if (event.has_ext4_es_lookup_extent_exit() &&
+             event.ext4_es_lookup_extent_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_es_lookup_extent_exit().ino());
+    return true;
+  } else if (event.has_ext4_es_remove_extent() &&
+             event.ext4_es_remove_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_es_remove_extent().ino());
+    return true;
+  } else if (event.has_ext4_evict_inode() && event.ext4_evict_inode().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_evict_inode().ino());
+    return true;
+  } else if (event.has_ext4_ext_convert_to_initialized_enter() &&
+             event.ext4_ext_convert_to_initialized_enter().ino()) {
+    *inode = static_cast<uint64_t>(
+        event.ext4_ext_convert_to_initialized_enter().ino());
+    return true;
+  } else if (event.has_ext4_ext_convert_to_initialized_fastpath() &&
+             event.ext4_ext_convert_to_initialized_fastpath().ino()) {
+    *inode = static_cast<uint64_t>(
+        event.ext4_ext_convert_to_initialized_fastpath().ino());
+    return true;
+  } else if (event.has_ext4_ext_handle_unwritten_extents() &&
+             event.ext4_ext_handle_unwritten_extents().ino()) {
+    *inode =
+        static_cast<uint64_t>(event.ext4_ext_handle_unwritten_extents().ino());
+    return true;
+  } else if (event.has_ext4_ext_in_cache() && event.ext4_ext_in_cache().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_in_cache().ino());
+    return true;
+  } else if (event.has_ext4_ext_load_extent() &&
+             event.ext4_ext_load_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_load_extent().ino());
+    return true;
+  } else if (event.has_ext4_ext_map_blocks_enter() &&
+             event.ext4_ext_map_blocks_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_map_blocks_enter().ino());
+    return true;
+  } else if (event.has_ext4_ext_map_blocks_exit() &&
+             event.ext4_ext_map_blocks_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_map_blocks_exit().ino());
+    return true;
+  } else if (event.has_ext4_ext_put_in_cache() &&
+             event.ext4_ext_put_in_cache().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_put_in_cache().ino());
+    return true;
+  } else if (event.has_ext4_ext_remove_space() &&
+             event.ext4_ext_remove_space().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_remove_space().ino());
+    return true;
+  } else if (event.has_ext4_ext_remove_space_done() &&
+             event.ext4_ext_remove_space_done().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_remove_space_done().ino());
+    return true;
+  } else if (event.has_ext4_ext_rm_idx() && event.ext4_ext_rm_idx().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_rm_idx().ino());
+    return true;
+  } else if (event.has_ext4_ext_rm_leaf() && event.ext4_ext_rm_leaf().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_rm_leaf().ino());
+    return true;
+  } else if (event.has_ext4_ext_show_extent() &&
+             event.ext4_ext_show_extent().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ext_show_extent().ino());
+    return true;
+  } else if (event.has_ext4_fallocate_enter() &&
+             event.ext4_fallocate_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_fallocate_enter().ino());
+    return true;
+  } else if (event.has_ext4_fallocate_exit() &&
+             event.ext4_fallocate_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_fallocate_exit().ino());
+    return true;
+  } else if (event.has_ext4_find_delalloc_range() &&
+             event.ext4_find_delalloc_range().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_find_delalloc_range().ino());
+    return true;
+  } else if (event.has_ext4_forget() && event.ext4_forget().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_forget().ino());
+    return true;
+  } else if (event.has_ext4_free_blocks() && event.ext4_free_blocks().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_free_blocks().ino());
+    return true;
+  } else if (event.has_ext4_free_inode() && event.ext4_free_inode().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_free_inode().ino());
+    return true;
+  } else if (event.has_ext4_get_reserved_cluster_alloc() &&
+             event.ext4_get_reserved_cluster_alloc().ino()) {
+    *inode =
+        static_cast<uint64_t>(event.ext4_get_reserved_cluster_alloc().ino());
+    return true;
+  } else if (event.has_ext4_ind_map_blocks_enter() &&
+             event.ext4_ind_map_blocks_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ind_map_blocks_enter().ino());
+    return true;
+  } else if (event.has_ext4_ind_map_blocks_exit() &&
+             event.ext4_ind_map_blocks_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_ind_map_blocks_exit().ino());
+    return true;
+  } else if (event.has_ext4_insert_range() && event.ext4_insert_range().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_insert_range().ino());
+    return true;
+  } else if (event.has_ext4_invalidatepage() &&
+             event.ext4_invalidatepage().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_invalidatepage().ino());
+    return true;
+  } else if (event.has_ext4_journalled_invalidatepage() &&
+             event.ext4_journalled_invalidatepage().ino()) {
+    *inode =
+        static_cast<uint64_t>(event.ext4_journalled_invalidatepage().ino());
+    return true;
+  } else if (event.has_ext4_journalled_write_end() &&
+             event.ext4_journalled_write_end().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_journalled_write_end().ino());
+    return true;
+  } else if (event.has_ext4_load_inode() && event.ext4_load_inode().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_load_inode().ino());
+    return true;
+  } else if (event.has_ext4_mark_inode_dirty() &&
+             event.ext4_mark_inode_dirty().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mark_inode_dirty().ino());
+    return true;
+  } else if (event.has_ext4_mb_new_group_pa() &&
+             event.ext4_mb_new_group_pa().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mb_new_group_pa().ino());
+    return true;
+  } else if (event.has_ext4_mb_new_inode_pa() &&
+             event.ext4_mb_new_inode_pa().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mb_new_inode_pa().ino());
+    return true;
+  } else if (event.has_ext4_mb_release_inode_pa() &&
+             event.ext4_mb_release_inode_pa().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mb_release_inode_pa().ino());
+    return true;
+  } else if (event.has_ext4_mballoc_alloc() &&
+             event.ext4_mballoc_alloc().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mballoc_alloc().ino());
+    return true;
+  } else if (event.has_ext4_mballoc_discard() &&
+             event.ext4_mballoc_discard().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mballoc_discard().ino());
+    return true;
+  } else if (event.has_ext4_mballoc_free() && event.ext4_mballoc_free().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mballoc_free().ino());
+    return true;
+  } else if (event.has_ext4_mballoc_prealloc() &&
+             event.ext4_mballoc_prealloc().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_mballoc_prealloc().ino());
+    return true;
+  } else if (event.has_ext4_other_inode_update_time() &&
+             event.ext4_other_inode_update_time().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_other_inode_update_time().ino());
+    return true;
+  } else if (event.has_ext4_other_inode_update_time() &&
+             event.ext4_other_inode_update_time().orig_ino()) {
+    *inode =
+        static_cast<uint64_t>(event.ext4_other_inode_update_time().orig_ino());
+    return true;
+  } else if (event.has_ext4_punch_hole() && event.ext4_punch_hole().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_punch_hole().ino());
+    return true;
+  } else if (event.has_ext4_readpage() && event.ext4_readpage().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_readpage().ino());
+    return true;
+  } else if (event.has_ext4_releasepage() && event.ext4_releasepage().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_releasepage().ino());
+    return true;
+  } else if (event.has_ext4_remove_blocks() &&
+             event.ext4_remove_blocks().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_remove_blocks().ino());
+    return true;
+  } else if (event.has_ext4_request_blocks() &&
+             event.ext4_request_blocks().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_request_blocks().ino());
+    return true;
+  } else if (event.has_ext4_sync_file_enter() &&
+             event.ext4_sync_file_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_sync_file_enter().ino());
+    return true;
+  } else if (event.has_ext4_sync_file_exit() &&
+             event.ext4_sync_file_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_sync_file_exit().ino());
+    return true;
+  } else if (event.has_ext4_truncate_enter() &&
+             event.ext4_truncate_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_truncate_enter().ino());
+    return true;
+  } else if (event.has_ext4_truncate_exit() &&
+             event.ext4_truncate_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_truncate_exit().ino());
+    return true;
+  } else if (event.has_ext4_unlink_enter() && event.ext4_unlink_enter().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_unlink_enter().ino());
+    return true;
+  } else if (event.has_ext4_unlink_exit() && event.ext4_unlink_exit().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_unlink_exit().ino());
+    return true;
+  } else if (event.has_ext4_write_begin() && event.ext4_write_begin().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_write_begin().ino());
+    return true;
+  } else if (event.has_ext4_write_end() && event.ext4_write_end().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_write_end().ino());
+    return true;
+  } else if (event.has_ext4_writepage() && event.ext4_writepage().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_writepage().ino());
+    return true;
+  } else if (event.has_ext4_writepages() && event.ext4_writepages().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_writepages().ino());
+    return true;
+  } else if (event.has_ext4_writepages_result() &&
+             event.ext4_writepages_result().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_writepages_result().ino());
+    return true;
+  } else if (event.has_ext4_zero_range() && event.ext4_zero_range().ino()) {
+    *inode = static_cast<uint64_t>(event.ext4_zero_range().ino());
+    return true;
+  } else if (event.has_mm_filemap_add_to_page_cache() &&
+             event.mm_filemap_add_to_page_cache().i_ino()) {
+    *inode =
+        static_cast<uint64_t>(event.mm_filemap_add_to_page_cache().i_ino());
+    return true;
+  } else if (event.has_mm_filemap_delete_from_page_cache() &&
+             event.mm_filemap_delete_from_page_cache().i_ino()) {
+    *inode = static_cast<uint64_t>(
+        event.mm_filemap_delete_from_page_cache().i_ino());
+    return true;
+  }
+  return false;
+}
+
+}  // namespace perfetto
diff --git a/tools/trace_to_text/ftrace_inode_handler.h b/tools/trace_to_text/ftrace_inode_handler.h
new file mode 100644
index 0000000..77b5a8a
--- /dev/null
+++ b/tools/trace_to_text/ftrace_inode_handler.h
@@ -0,0 +1,30 @@
+/*
+ * 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 TOOLS_TRACE_TO_TEXT_FTRACE_INODE_HANDLER_H_
+#define TOOLS_TRACE_TO_TEXT_FTRACE_INODE_HANDLER_H_
+
+#include "tools/trace_to_text/ftrace_inode_handler.h"
+
+#include "perfetto/trace/trace_packet.pb.h"
+
+namespace perfetto {
+
+bool ParseInode(const protos::FtraceEvent&, uint64_t* inode);
+
+}  // namespace perfetto
+
+#endif  // TOOLS_TRACE_TO_TEXT_FTRACE_INODE_HANDLER_H_
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 8769466..bd29756 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -22,6 +22,7 @@
 
 #include <algorithm>
 #include <fstream>
+#include <functional>
 #include <iostream>
 #include <istream>
 #include <limits>
@@ -29,7 +30,6 @@
 #include <memory>
 #include <ostream>
 #include <sstream>
-#include <string>
 #include <utility>
 
 #include <google/protobuf/compiler/importer.h>
@@ -43,6 +43,7 @@
 #include "perfetto/trace/trace.pb.h"
 #include "perfetto/trace/trace_packet.pb.h"
 #include "tools/trace_to_text/ftrace_event_formatter.h"
+#include "tools/trace_to_text/ftrace_inode_handler.h"
 
 namespace perfetto {
 namespace {
@@ -70,8 +71,6 @@
     "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\\n"
     "#              | |        |      |   ||||       |         |\\n";
 
-constexpr const char* inodeFileTypeArray[] = {"UNKNOWN", "FILE", "DIRECTORY"};
-
 using google::protobuf::Descriptor;
 using google::protobuf::DynamicMessageFactory;
 using google::protobuf::FileDescriptor;
@@ -120,61 +119,6 @@
   }
 };
 
-uint64_t TimestampToSeconds(uint64_t timestamp) {
-  return timestamp / 1000000000ul;
-}
-
-uint64_t TimestampToMicroseconds(uint64_t timestamp) {
-  return (timestamp / 1000) % 1000000ul;
-}
-
-std::string FormatPrefix(uint64_t timestamp, uint64_t cpu) {
-  char line[2048];
-  uint64_t seconds = TimestampToSeconds(timestamp);
-  uint64_t useconds = TimestampToMicroseconds(timestamp);
-  sprintf(line,
-          "<idle>-0     (-----) [%03" PRIu64 "] d..3 %" PRIu64 ".%.6" PRIu64
-          ": ",
-          cpu, seconds, useconds);
-  return std::string(line);
-}
-
-// TODO(taylori): Confirm correct format for this.
-// Calling this breaks loading into chrome://tracing
-std::string FormatProcess(const Process& process) {
-  char line[2048];
-  sprintf(line, "process: pid=%d ppid=%d cmdline=", process.pid(),
-          process.ppid());
-  std::string output = std::string(line);
-  for (auto field : process.cmdline()) {
-    char cmd[2048];
-    sprintf(cmd, "%s ", field.c_str());
-    output += std::string(cmd);
-  }
-  output += "\\n";
-  for (auto thread : process.threads()) {
-    char thread_line[2048];
-    sprintf(thread_line, "thread: tid=%d name=%s\\n", thread.tid(),
-            thread.name().c_str());
-    output += thread_line;
-  }
-  return output;
-}
-
-// Calling this breaks loading into chrome://tracing
-std::string FormatInodeFileMap(const Entry& entry) {
-  char line[2048];
-  sprintf(line, "inode_file_map: ino=%llu type=%s path=", entry.inode_number(),
-          inodeFileTypeArray[entry.type()]);
-  std::string output = std::string(line);
-  for (auto field : entry.paths()) {
-    char path[2048];
-    sprintf(path, "%s", field.c_str());
-    output += std::string(path);
-  }
-  return output;
-}
-
 void ForEachPacketInTrace(
     std::istream* input,
     const std::function<void(const protos::TracePacket&)>& f) {
@@ -220,7 +164,9 @@
   }
 }
 
-int TraceToSystrace(std::istream* input, std::ostream* output) {
+int TraceToSystrace(std::istream* input,
+                    std::ostream* output,
+                    bool wrap_in_json) {
   std::multimap<uint64_t, std::string> sorted;
 
   ForEachPacketInTrace(input, [&sorted](const protos::TracePacket& packet) {
@@ -237,22 +183,25 @@
     }
   });
 
-  *output << kTraceHeader;
-  *output << kFtraceHeader;
+  if (wrap_in_json) {
+    *output << kTraceHeader;
+    *output << kFtraceHeader;
+  }
 
   fprintf(stderr, "\n");
   size_t total_events = sorted.size();
   size_t written_events = 0;
   for (auto it = sorted.begin(); it != sorted.end(); it++) {
-    *output << it->second;
-    if (written_events++ % 100 == 0) {
+    *output << it->second << (wrap_in_json ? "\\n" : "\n");
+    if (written_events++ % 100 == 0 && !isatty(STDOUT_FILENO)) {
       fprintf(stderr, "Writing trace: %.2f %%\r",
               written_events * 100.0 / total_events);
       fflush(stderr);
     }
   }
 
-  *output << kTraceFooter;
+  if (wrap_in_json)
+    *output << kTraceFooter;
 
   return 0;
 }
@@ -279,33 +228,10 @@
   return 0;
 }
 
-int TraceToSummary(std::istream* input, std::ostream* output) {
-  uint64_t start = std::numeric_limits<uint64_t>::max();
-  uint64_t end = 0;
-  std::multiset<uint64_t> ftrace_timestamps;
-
-  ForEachPacketInTrace(input, [&start, &end, &ftrace_timestamps](
-                                  const protos::TracePacket& packet) {
-    if (!packet.has_ftrace_events())
-      return;
-
-    const FtraceEventBundle& bundle = packet.ftrace_events();
-
-    for (const FtraceEvent& event : bundle.event()) {
-      if (event.timestamp()) {
-        start = std::min<uint64_t>(start, event.timestamp());
-        end = std::max<uint64_t>(end, event.timestamp());
-        ftrace_timestamps.insert(event.timestamp());
-      }
-    }
-  });
-
-  fprintf(stderr, "\n");
-
-  char line[2048];
-  sprintf(line, "Duration: %" PRIu64 "ms\n", (end - start) / (1000 * 1000));
-  *output << std::string(line);
-
+void PrintFtraceTrack(std::ostream* output,
+                      const uint64_t& start,
+                      const uint64_t& end,
+                      const std::multiset<uint64_t>& ftrace_timestamps) {
   constexpr char kFtraceTrackName[] = "ftrace ";
   size_t width = GetWidth();
   size_t bucket_count = width - strlen(kFtraceTrackName);
@@ -321,14 +247,159 @@
 
   std::vector<std::string> out =
       std::vector<std::string>({" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇"});
-  *output << kFtraceTrackName;
+  *output << "-------------------- " << kFtraceTrackName
+          << "--------------------\n";
+  char line[2048];
   for (size_t i = 0; i < bucket_count; i++) {
     sprintf(
         line, "%s",
         out[std::min(buckets[i] / (max / out.size()), out.size() - 1)].c_str());
     *output << std::string(line);
   }
+  *output << "\n\n";
+}
+
+void PrintInodeStats(std::ostream* output,
+                     const std::set<uint64_t>& ftrace_inodes,
+                     const uint64_t& ftrace_inode_count,
+                     const std::set<uint64_t>& resolved_map_inodes,
+                     const std::set<uint64_t>& resolved_scan_inodes) {
+  *output << "--------------------Inode Stats-------------------\n";
+  char line[2048];
+
+  sprintf(line, "Events with inodes: %" PRIu64 "\n", ftrace_inode_count);
+  *output << std::string(line);
+
+  sprintf(line, "Unique inodes from events: %zu\n", ftrace_inodes.size());
+  *output << std::string(line);
+
+  sprintf(line, "Resolved inodes from static map: %zu\n",
+          resolved_map_inodes.size());
+  *output << std::string(line);
+
+  sprintf(line, "Resolved inodes from scan and cache: %zu\n",
+          resolved_scan_inodes.size());
+  *output << std::string(line);
+
+  std::set<uint64_t> resolved_inodes;
+  set_union(resolved_map_inodes.begin(), resolved_map_inodes.end(),
+            resolved_scan_inodes.begin(), resolved_scan_inodes.end(),
+            std::inserter(resolved_inodes, resolved_inodes.begin()));
+
+  sprintf(line, "Total resolved inodes: %zu\n", resolved_inodes.size());
+  *output << std::string(line);
+
+  std::set<uint64_t> intersect;
+  set_intersection(resolved_inodes.begin(), resolved_inodes.end(),
+                   ftrace_inodes.begin(), ftrace_inodes.end(),
+                   std::inserter(intersect, intersect.begin()));
+
+  sprintf(line, "Unresolved inodes: %zu\n",
+          ftrace_inodes.size() - intersect.size());
+
+  sprintf(line, "Unexpected inodes from filesystem: %zu\n",
+          resolved_inodes.size() - intersect.size());
+  *output << std::string(line);
+
   *output << "\n";
+}
+
+void PrintProcessStats(std::ostream* output,
+                       const std::set<pid_t>& tids_in_tree,
+                       const std::set<pid_t>& tids_in_events) {
+  *output << "----------------Process Tree Stats----------------\n";
+
+  char tid[2048];
+  sprintf(tid, "Unique thread ids in process tree: %zu\n", tids_in_tree.size());
+  *output << std::string(tid);
+
+  char tid_event[2048];
+  sprintf(tid_event, "Unique thread ids in ftrace events: %zu\n",
+          tids_in_events.size());
+  *output << std::string(tid_event);
+
+  std::set<pid_t> intersect;
+  set_intersection(tids_in_tree.begin(), tids_in_tree.end(),
+                   tids_in_events.begin(), tids_in_events.end(),
+                   std::inserter(intersect, intersect.begin()));
+
+  char matching[2048];
+  sprintf(matching, "Thread ids with process info: %zu/%zu -> %zu %%\n\n",
+          intersect.size(), tids_in_events.size(),
+          (intersect.size() * 100) / tids_in_events.size());
+  *output << std::string(matching);
+  *output << "\n";
+}
+
+int TraceToSummary(std::istream* input, std::ostream* output) {
+  uint64_t start = std::numeric_limits<uint64_t>::max();
+  uint64_t end = 0;
+  std::multiset<uint64_t> ftrace_timestamps;
+  std::set<pid_t> tids_in_tree;
+  std::set<pid_t> tids_in_events;
+  std::set<uint64_t> ftrace_inodes;
+  uint64_t ftrace_inode_count = 0;
+  std::set<uint64_t> resolved_map_inodes;
+  std::set<uint64_t> resolved_scan_inodes;
+
+  ForEachPacketInTrace(
+      input, [&start, &end, &ftrace_timestamps, &tids_in_tree, &tids_in_events,
+              &ftrace_inodes, &ftrace_inode_count, &resolved_map_inodes,
+              &resolved_scan_inodes](const protos::TracePacket& packet) {
+
+        if (packet.has_process_tree()) {
+          const ProcessTree& tree = packet.process_tree();
+          for (Process process : tree.processes()) {
+            for (ProcessTree::Thread thread : process.threads()) {
+              tids_in_tree.insert(thread.tid());
+            }
+          }
+        }
+
+        if (packet.has_inode_file_map()) {
+          const InodeFileMap& inode_file_map = packet.inode_file_map();
+          const auto& mount_points = inode_file_map.mount_points();
+          bool from_scan = std::find(mount_points.begin(), mount_points.end(),
+                                     "/data") != mount_points.end();
+          for (const auto& entry : inode_file_map.entries())
+            if (from_scan)
+              resolved_scan_inodes.insert(entry.inode_number());
+            else
+              resolved_map_inodes.insert(entry.inode_number());
+        }
+
+        if (!packet.has_ftrace_events())
+          return;
+
+        const FtraceEventBundle& bundle = packet.ftrace_events();
+        uint64_t inode_number = 0;
+        for (const FtraceEvent& event : bundle.event()) {
+          if (ParseInode(event, &inode_number)) {
+            ftrace_inodes.insert(inode_number);
+            ftrace_inode_count++;
+          }
+          if (event.pid()) {
+            tids_in_events.insert(event.pid());
+          }
+          if (event.timestamp()) {
+            start = std::min<uint64_t>(start, event.timestamp());
+            end = std::max<uint64_t>(end, event.timestamp());
+            ftrace_timestamps.insert(event.timestamp());
+          }
+        }
+      });
+
+  fprintf(stderr, "\n");
+
+  char line[2048];
+  sprintf(line, "Duration: %" PRIu64 "ms\n", (end - start) / (1000 * 1000));
+  *output << std::string(line);
+
+  PrintFtraceTrack(output, start, end, ftrace_timestamps);
+  PrintProcessStats(output, tids_in_tree, tids_in_events);
+  PrintInodeStats(output, ftrace_inodes, ftrace_inode_count,
+                  resolved_map_inodes, resolved_scan_inodes);
+
   return 0;
 }
 
@@ -338,7 +409,7 @@
 namespace {
 
 int Usage(int argc, char** argv) {
-  printf("Usage: %s [systrace|text|summary] < trace.proto > trace.txt\n",
+  printf("Usage: %s [systrace|json|text|summary] < trace.proto > trace.txt\n",
          argv[0]);
   return 1;
 }
@@ -351,15 +422,15 @@
 
   std::string format(argv[1]);
 
-  bool is_systrace = format == "systrace";
-  bool is_text = format == "text";
-  bool is_summary = format == "summary";
-
-  if (is_systrace)
-    return perfetto::TraceToSystrace(&std::cin, &std::cout);
-  if (is_text)
+  if (format == "json")
+    return perfetto::TraceToSystrace(&std::cin, &std::cout,
+                                     /*wrap_in_json=*/true);
+  if (format == "systrace")
+    return perfetto::TraceToSystrace(&std::cin, &std::cout,
+                                     /*wrap_in_json=*/false);
+  if (format == "text")
     return perfetto::TraceToText(&std::cin, &std::cout);
-  if (is_summary)
+  if (format == "summary")
     return perfetto::TraceToSummary(&std::cin, &std::cout);
   return Usage(argc, argv);
 }