Merge "Project import generated by Copybara."
diff --git a/Android.bp b/Android.bp
index 4ebd425..de0d6e9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4030,6 +4030,165 @@
   ],
 }
 
+// GN: //protos/perfetto/trace:descriptor
+genrule {
+  name: "perfetto_protos_perfetto_trace_descriptor",
+  srcs: [
+    "protos/perfetto/common/android_energy_consumer_descriptor.proto",
+    "protos/perfetto/common/android_log_constants.proto",
+    "protos/perfetto/common/builtin_clock.proto",
+    "protos/perfetto/common/commit_data_request.proto",
+    "protos/perfetto/common/data_source_descriptor.proto",
+    "protos/perfetto/common/descriptor.proto",
+    "protos/perfetto/common/gpu_counter_descriptor.proto",
+    "protos/perfetto/common/interceptor_descriptor.proto",
+    "protos/perfetto/common/observable_events.proto",
+    "protos/perfetto/common/perf_events.proto",
+    "protos/perfetto/common/sys_stats_counters.proto",
+    "protos/perfetto/common/trace_stats.proto",
+    "protos/perfetto/common/tracing_service_capabilities.proto",
+    "protos/perfetto/common/tracing_service_state.proto",
+    "protos/perfetto/common/track_event_descriptor.proto",
+    "protos/perfetto/config/android/android_log_config.proto",
+    "protos/perfetto/config/android/android_polled_state_config.proto",
+    "protos/perfetto/config/android/packages_list_config.proto",
+    "protos/perfetto/config/chrome/chrome_config.proto",
+    "protos/perfetto/config/data_source_config.proto",
+    "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/gpu/gpu_counter_config.proto",
+    "protos/perfetto/config/gpu/vulkan_memory_config.proto",
+    "protos/perfetto/config/inode_file/inode_file_config.proto",
+    "protos/perfetto/config/interceptor_config.proto",
+    "protos/perfetto/config/interceptors/console_config.proto",
+    "protos/perfetto/config/power/android_power_config.proto",
+    "protos/perfetto/config/process_stats/process_stats_config.proto",
+    "protos/perfetto/config/profiling/heapprofd_config.proto",
+    "protos/perfetto/config/profiling/java_hprof_config.proto",
+    "protos/perfetto/config/profiling/perf_event_config.proto",
+    "protos/perfetto/config/stress_test_config.proto",
+    "protos/perfetto/config/sys_stats/sys_stats_config.proto",
+    "protos/perfetto/config/test_config.proto",
+    "protos/perfetto/config/trace_config.proto",
+    "protos/perfetto/config/track_event/track_event_config.proto",
+    "protos/perfetto/trace/android/android_log.proto",
+    "protos/perfetto/trace/android/frame_timeline_event.proto",
+    "protos/perfetto/trace/android/gpu_mem_event.proto",
+    "protos/perfetto/trace/android/graphics_frame_event.proto",
+    "protos/perfetto/trace/android/initial_display_state.proto",
+    "protos/perfetto/trace/android/packages_list.proto",
+    "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto",
+    "protos/perfetto/trace/chrome/chrome_metadata.proto",
+    "protos/perfetto/trace/chrome/chrome_trace_event.proto",
+    "protos/perfetto/trace/clock_snapshot.proto",
+    "protos/perfetto/trace/extension_descriptor.proto",
+    "protos/perfetto/trace/filesystem/inode_file_map.proto",
+    "protos/perfetto/trace/ftrace/binder.proto",
+    "protos/perfetto/trace/ftrace/block.proto",
+    "protos/perfetto/trace/ftrace/cgroup.proto",
+    "protos/perfetto/trace/ftrace/clk.proto",
+    "protos/perfetto/trace/ftrace/compaction.proto",
+    "protos/perfetto/trace/ftrace/cpuhp.proto",
+    "protos/perfetto/trace/ftrace/dmabuf_heap.proto",
+    "protos/perfetto/trace/ftrace/dpu.proto",
+    "protos/perfetto/trace/ftrace/ext4.proto",
+    "protos/perfetto/trace/ftrace/f2fs.proto",
+    "protos/perfetto/trace/ftrace/fastrpc.proto",
+    "protos/perfetto/trace/ftrace/fence.proto",
+    "protos/perfetto/trace/ftrace/filemap.proto",
+    "protos/perfetto/trace/ftrace/ftrace.proto",
+    "protos/perfetto/trace/ftrace/ftrace_event.proto",
+    "protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
+    "protos/perfetto/trace/ftrace/ftrace_stats.proto",
+    "protos/perfetto/trace/ftrace/g2d.proto",
+    "protos/perfetto/trace/ftrace/generic.proto",
+    "protos/perfetto/trace/ftrace/gpu_mem.proto",
+    "protos/perfetto/trace/ftrace/i2c.proto",
+    "protos/perfetto/trace/ftrace/ion.proto",
+    "protos/perfetto/trace/ftrace/ipi.proto",
+    "protos/perfetto/trace/ftrace/irq.proto",
+    "protos/perfetto/trace/ftrace/kmem.proto",
+    "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+    "protos/perfetto/trace/ftrace/mali.proto",
+    "protos/perfetto/trace/ftrace/mdss.proto",
+    "protos/perfetto/trace/ftrace/mm_event.proto",
+    "protos/perfetto/trace/ftrace/oom.proto",
+    "protos/perfetto/trace/ftrace/power.proto",
+    "protos/perfetto/trace/ftrace/raw_syscalls.proto",
+    "protos/perfetto/trace/ftrace/regulator.proto",
+    "protos/perfetto/trace/ftrace/sched.proto",
+    "protos/perfetto/trace/ftrace/scm.proto",
+    "protos/perfetto/trace/ftrace/sde.proto",
+    "protos/perfetto/trace/ftrace/signal.proto",
+    "protos/perfetto/trace/ftrace/sync.proto",
+    "protos/perfetto/trace/ftrace/systrace.proto",
+    "protos/perfetto/trace/ftrace/task.proto",
+    "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
+    "protos/perfetto/trace/ftrace/thermal.proto",
+    "protos/perfetto/trace/ftrace/vmscan.proto",
+    "protos/perfetto/trace/ftrace/workqueue.proto",
+    "protos/perfetto/trace/gpu/gpu_counter_event.proto",
+    "protos/perfetto/trace/gpu/gpu_log.proto",
+    "protos/perfetto/trace/gpu/gpu_render_stage_event.proto",
+    "protos/perfetto/trace/gpu/vulkan_api_event.proto",
+    "protos/perfetto/trace/gpu/vulkan_memory_event.proto",
+    "protos/perfetto/trace/interned_data/interned_data.proto",
+    "protos/perfetto/trace/memory_graph.proto",
+    "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
+    "protos/perfetto/trace/perfetto/tracing_service_event.proto",
+    "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+    "protos/perfetto/trace/power/battery_counters.proto",
+    "protos/perfetto/trace/power/power_rails.proto",
+    "protos/perfetto/trace/profiling/deobfuscation.proto",
+    "protos/perfetto/trace/profiling/heap_graph.proto",
+    "protos/perfetto/trace/profiling/profile_common.proto",
+    "protos/perfetto/trace/profiling/profile_packet.proto",
+    "protos/perfetto/trace/profiling/smaps.proto",
+    "protos/perfetto/trace/ps/process_stats.proto",
+    "protos/perfetto/trace/ps/process_tree.proto",
+    "protos/perfetto/trace/sys_stats/sys_stats.proto",
+    "protos/perfetto/trace/system_info.proto",
+    "protos/perfetto/trace/system_info/cpu_info.proto",
+    "protos/perfetto/trace/test_event.proto",
+    "protos/perfetto/trace/test_extensions.proto",
+    "protos/perfetto/trace/trace.proto",
+    "protos/perfetto/trace/trace_packet.proto",
+    "protos/perfetto/trace/trace_packet_defaults.proto",
+    "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
+    "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
+    "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
+    "protos/perfetto/trace/track_event/chrome_frame_reporter.proto",
+    "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
+    "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
+    "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
+    "protos/perfetto/trace/track_event/chrome_message_pump.proto",
+    "protos/perfetto/trace/track_event/chrome_mojo_event_info.proto",
+    "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
+    "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto",
+    "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
+    "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
+    "protos/perfetto/trace/track_event/debug_annotation.proto",
+    "protos/perfetto/trace/track_event/log_message.proto",
+    "protos/perfetto/trace/track_event/process_descriptor.proto",
+    "protos/perfetto/trace/track_event/source_location.proto",
+    "protos/perfetto/trace/track_event/task_execution.proto",
+    "protos/perfetto/trace/track_event/thread_descriptor.proto",
+    "protos/perfetto/trace/track_event/track_descriptor.proto",
+    "protos/perfetto/trace/track_event/track_event.proto",
+    "protos/perfetto/trace/trigger.proto",
+    "protos/perfetto/trace/ui_state.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
+  out: [
+    "perfetto_protos_perfetto_trace_descriptor.bin",
+  ],
+}
+
 // GN: //protos/perfetto/trace/filesystem:cpp
 genrule {
   name: "perfetto_protos_perfetto_trace_filesystem_cpp_gen",
@@ -7335,6 +7494,14 @@
   ],
 }
 
+// GN: //src/protozero:proto_ring_buffer
+filegroup {
+  name: "perfetto_src_protozero_proto_ring_buffer",
+  srcs: [
+    "src/protozero/proto_ring_buffer.cc",
+  ],
+}
+
 // GN: //src/protozero/protoc_plugin:cppgen_plugin
 cc_binary_host {
   name: "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
@@ -7558,6 +7725,7 @@
     "src/protozero/message_handle_unittest.cc",
     "src/protozero/message_unittest.cc",
     "src/protozero/proto_decoder_unittest.cc",
+    "src/protozero/proto_ring_buffer_unittest.cc",
     "src/protozero/proto_utils_unittest.cc",
     "src/protozero/scattered_stream_writer_unittest.cc",
     "src/protozero/test/cppgen_conformance_unittest.cc",
@@ -7897,7 +8065,6 @@
 filegroup {
   name: "perfetto_src_trace_processor_rpc_rpc",
   srcs: [
-    "src/trace_processor/rpc/proto_ring_buffer.cc",
     "src/trace_processor/rpc/query_result_serializer.cc",
     "src/trace_processor/rpc/rpc.cc",
   ],
@@ -7907,7 +8074,6 @@
 filegroup {
   name: "perfetto_src_trace_processor_rpc_unittests",
   srcs: [
-    "src/trace_processor/rpc/proto_ring_buffer_unittest.cc",
     "src/trace_processor/rpc/query_result_serializer_unittest.cc",
   ],
 }
@@ -8648,6 +8814,7 @@
     "src/tracing/ipc/default_socket.cc",
     "src/tracing/ipc/memfd.cc",
     "src/tracing/ipc/posix_shared_memory.cc",
+    "src/tracing/ipc/shared_memory_windows.cc",
   ],
 }
 
@@ -8690,6 +8857,7 @@
   name: "perfetto_src_tracing_platform_impl",
   srcs: [
     "src/tracing/platform_posix.cc",
+    "src/tracing/platform_windows.cc",
   ],
 }
 
@@ -8799,6 +8967,21 @@
   ],
 }
 
+// GN: //tools/trace_to_text:gen_cc_trace_descriptor
+genrule {
+  name: "perfetto_tools_trace_to_text_gen_cc_trace_descriptor",
+  srcs: [
+    ":perfetto_protos_perfetto_trace_descriptor",
+  ],
+  cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+  out: [
+    "tools/trace_to_text/trace.descriptor.h",
+  ],
+  tool_files: [
+    "tools/gen_cc_proto_descriptor.py",
+  ],
+}
+
 // GN: //tools/trace_to_text:pprofbuilder
 filegroup {
   name: "perfetto_tools_trace_to_text_pprofbuilder",
@@ -9083,6 +9266,7 @@
     ":perfetto_src_protozero_filtering_filter_util",
     ":perfetto_src_protozero_filtering_message_filter",
     ":perfetto_src_protozero_filtering_unittests",
+    ":perfetto_src_protozero_proto_ring_buffer",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_protozero_testing_messages_cpp_gen",
     ":perfetto_src_protozero_testing_messages_lite_gen",
@@ -9609,6 +9793,7 @@
     "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
     "perfetto_src_trace_processor_metrics_gen_merged_sql_metrics",
+    "perfetto_tools_trace_to_text_gen_cc_trace_descriptor",
   ],
   defaults: [
     "perfetto_defaults",
diff --git a/BUILD b/BUILD
index 3440cca..45b742f 100644
--- a/BUILD
+++ b/BUILD
@@ -803,6 +803,15 @@
     ],
 )
 
+# GN target: //src/protozero:proto_ring_buffer
+filegroup(
+    name = "src_protozero_proto_ring_buffer",
+    srcs = [
+        "src/protozero/proto_ring_buffer.cc",
+        "src/protozero/proto_ring_buffer.h",
+    ],
+)
+
 # GN target: //src/trace_processor/analysis:analysis
 filegroup(
     name = "src_trace_processor_analysis_analysis",
@@ -1048,8 +1057,6 @@
 filegroup(
     name = "src_trace_processor_rpc_rpc",
     srcs = [
-        "src/trace_processor/rpc/proto_ring_buffer.cc",
-        "src/trace_processor/rpc/proto_ring_buffer.h",
         "src/trace_processor/rpc/query_result_serializer.cc",
         "src/trace_processor/rpc/query_result_serializer.h",
         "src/trace_processor/rpc/rpc.cc",
@@ -1681,6 +1688,8 @@
         "src/tracing/ipc/memfd.h",
         "src/tracing/ipc/posix_shared_memory.cc",
         "src/tracing/ipc/posix_shared_memory.h",
+        "src/tracing/ipc/shared_memory_windows.cc",
+        "src/tracing/ipc/shared_memory_windows.h",
     ],
 )
 
@@ -1735,6 +1744,7 @@
     name = "src_tracing_platform_impl",
     srcs = [
         "src/tracing/platform_posix.cc",
+        "src/tracing/platform_windows.cc",
     ],
 )
 
@@ -1777,6 +1787,16 @@
     ],
 )
 
+perfetto_cc_proto_descriptor(
+    name = "tools_trace_to_text_gen_cc_trace_descriptor",
+    deps = [
+        ":protos_perfetto_trace_descriptor",
+    ],
+    outs = [
+        "tools/trace_to_text/trace.descriptor.h",
+    ],
+)
+
 # GN target: //tools/trace_to_text:pprofbuilder
 filegroup(
     name = "tools_trace_to_text_pprofbuilder",
@@ -2615,6 +2635,17 @@
     ],
 )
 
+# GN target: //protos/perfetto/trace:descriptor
+perfetto_proto_descriptor(
+    name = "protos_perfetto_trace_descriptor",
+    deps = [
+        ":protos_perfetto_trace_protos",
+    ],
+    outs = [
+        "protos_perfetto_trace_descriptor.bin",
+    ],
+)
+
 # GN target: //protos/perfetto/trace/filesystem:lite
 perfetto_cc_proto_library(
     name = "protos_perfetto_trace_filesystem_lite",
@@ -3080,6 +3111,46 @@
     ],
 )
 
+# GN target: //protos/perfetto/trace:descriptor
+perfetto_proto_library(
+    name = "protos_perfetto_trace_protos",
+    srcs = [
+        "protos/perfetto/trace/trace.proto",
+    ],
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
+    deps = [
+        ":protos_perfetto_common_protos",
+        ":protos_perfetto_config_android_protos",
+        ":protos_perfetto_config_ftrace_protos",
+        ":protos_perfetto_config_gpu_protos",
+        ":protos_perfetto_config_inode_file_protos",
+        ":protos_perfetto_config_interceptors_protos",
+        ":protos_perfetto_config_power_protos",
+        ":protos_perfetto_config_process_stats_protos",
+        ":protos_perfetto_config_profiling_protos",
+        ":protos_perfetto_config_protos",
+        ":protos_perfetto_config_sys_stats_protos",
+        ":protos_perfetto_config_track_event_protos",
+        ":protos_perfetto_trace_android_protos",
+        ":protos_perfetto_trace_chrome_protos",
+        ":protos_perfetto_trace_filesystem_protos",
+        ":protos_perfetto_trace_ftrace_protos",
+        ":protos_perfetto_trace_gpu_protos",
+        ":protos_perfetto_trace_interned_data_protos",
+        ":protos_perfetto_trace_minimal_protos",
+        ":protos_perfetto_trace_non_minimal_protos",
+        ":protos_perfetto_trace_perfetto_protos",
+        ":protos_perfetto_trace_power_protos",
+        ":protos_perfetto_trace_profiling_protos",
+        ":protos_perfetto_trace_ps_protos",
+        ":protos_perfetto_trace_sys_stats_protos",
+        ":protos_perfetto_trace_system_info_protos",
+        ":protos_perfetto_trace_track_event_protos",
+    ],
+)
+
 # GN target: //protos/perfetto/trace/ps:lite
 perfetto_cc_proto_library(
     name = "protos_perfetto_trace_ps_lite",
@@ -3583,6 +3654,7 @@
         ":src_profiling_deobfuscator",
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
+        ":src_protozero_proto_ring_buffer",
         ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_db_db",
         ":src_trace_processor_export_json",
@@ -3834,6 +3906,7 @@
                ":src_trace_processor_importers_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
+               ":tools_trace_to_text_gen_cc_trace_descriptor",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.protobuf_full +
            PERFETTO_CONFIG.deps.sqlite +
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 98022c9..97ab40a 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -788,7 +788,9 @@
   defines = [ "HAVE_POSIX_REGEX" ]
   public_configs = [ ":benchmark_config" ]
   all_dependent_configs = [ ":benchmark_config" ]
-  cflags = [ "-Wno-deprecated-declarations" ]
+  if (!is_win) {
+    cflags = [ "-Wno-deprecated-declarations" ]
+  }
   configs -= [ "//gn/standalone:extra_warnings" ]
   deps = [ "//gn:default_deps" ]
 }
@@ -1056,10 +1058,11 @@
 
 config("jsoncpp_config") {
   visibility = _buildtools_visibility
-  cflags = [
-    "-DJSON_USE_EXCEPTION=0",
-    "-Wno-deprecated-declarations",
-
+  cflags = [ "-DJSON_USE_EXCEPTION=0" ]
+  if (!is_win) {
+    cflags += [ "-Wno-deprecated-declarations" ]
+  }
+  cflags += [
     # Using -isystem instead of include_dirs (-I), so we don't need to suppress
     # warnings coming from third-party headers. Doing so would mask warnings in
     # our own code.
diff --git a/docs/data-sources/cpu-scheduling.md b/docs/data-sources/cpu-scheduling.md
index 8b771e8..29a2144 100644
--- a/docs/data-sources/cpu-scheduling.md
+++ b/docs/data-sources/cpu-scheduling.md
@@ -147,3 +147,45 @@
 
 ![](/docs/images/latency.png "Scheduling wake-up events in the UI")
 
+### Decoding `end_state`
+
+The [sched_slice](/docs/analysis/sql-tables.autogen#sched_slice) table contains
+information on scheduling activity of the system:
+
+```
+> select * from sched_slice limit 1
+id  type        ts          dur    cpu utid end_state priority
+0   sched_slice 70730062200 125364 0   1    S         130     
+```
+
+Each row of the table shows when a given thread (`utid`) began running
+(`ts`), on which core it ran (`cpu`), for how long it ran (`dur`), 
+and why it stopped running: `end_state`.
+
+`end_state` is encoded as one or more ascii characters. The UI uses
+the following translations to convert `end_state` into human readable
+text:
+
+| end_state  | Translation            |
+|------------|------------------------|
+| R          | Runnable               |
+| S          | Sleeping               |
+| D          | Uninterruptible Sleep |
+| T          | Stopped                |
+| t          | Traced                 |
+| X          | Exit (Dead)            |
+| Z          | Exit (Zombie)          |
+| x          | Task Dead              |
+| I          | Task Dead              |
+| K          | Wake Kill              |
+| W          | Waking                 |
+| P          | Parked                 |
+| N          | No Load                |
+| +          | (Preempted)            |
+
+Not all combinations of characters are meaningful.
+
+If we do not know when the scheduling ended (for example because the
+trace ended while the thread was still running) `end_state` will be
+`NULL` and `dur` will be -1.
+
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index c1ff723..d7263e7 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -144,9 +144,12 @@
   # system backend in the client library.
   # This includes building things that rely on POSIX sockets, this places
   # limitations on the supported operating systems.
-  enable_perfetto_ipc = !is_win && !is_fuchsia && !is_nacl &&
-                        (perfetto_build_standalone ||
-                         perfetto_build_with_android || build_with_chromium)
+  # For now the IPC layer is conservatively not enabled on Chromium+Windows
+  # builds.
+  enable_perfetto_ipc =
+      !is_fuchsia && !is_nacl &&
+      (perfetto_build_standalone || perfetto_build_with_android ||
+       (build_with_chromium && !is_win))
 
   # Makes the heap profiling daemon target reachable. It works only on Android,
   # but is built on Linux as well for test/compiler coverage.
@@ -186,7 +189,7 @@
                               build_with_chromium || perfetto_build_with_android
 
   enable_perfetto_integration_tests =
-      (perfetto_build_standalone && !is_win) || perfetto_build_with_android
+      perfetto_build_standalone || perfetto_build_with_android
 
   enable_perfetto_benchmarks = perfetto_build_standalone && !is_win
 
@@ -314,9 +317,6 @@
 # |is_perfetto_build_generator| must be true.
 assert(!perfetto_build_with_android || is_perfetto_build_generator)
 
-# The IPC layer based on UNIX sockets can't be built on Win.
-assert(!enable_perfetto_ipc || !is_win)
-
 # We should never end up in a state where is_perfetto_embedder=true but
 # perfetto_build_with_embedder=false.
 assert(!is_perfetto_embedder || perfetto_build_with_embedder)
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 03d6455..5b87753 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -173,6 +173,17 @@
     } else {
       deps += [ "$perfetto_root_path/src/ipc:common" ]
     }
+    if (is_win) {
+      # TODO(primiano): investigate this. In Windows standalone builds, some
+      # executable targets end up in a state where no code pulls a dep on the
+      # ipc:client (in turn that seems a subtle consequence of not having
+      # traced_probes on Windows). This dep here is effectively needed because
+      # the client-side code in the generated .ipc.cc effectively depends on the
+      # client-side IPC library. Perhaps we just should do this unconditionally
+      # on all platforms?
+      deps += [ "$perfetto_root_path/src/ipc:client" ]
+    }
+
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index af70070..31941ed 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -161,6 +161,8 @@
     cflags += [
       "/bigobj",  # Some of our files are bigger than the regular limits.
       "/Gy",  # Enable function-level linking.
+      "/FS",  # Preserve previous PDB behavior.
+      "/utf-8",  # Assume UTF-8 by default to avoid code page dependencies.
     ]
     defines += [
       "_CRT_NONSTDC_NO_WARNINGS",
diff --git a/include/perfetto/ext/base/getopt.h b/include/perfetto/ext/base/getopt.h
index 77c1490..bf993fc 100644
--- a/include/perfetto/ext/base/getopt.h
+++ b/include/perfetto/ext/base/getopt.h
@@ -34,8 +34,10 @@
 // replacement to the various main.cc, which can't know about the nested
 // namespace.
 using ::perfetto::base::getopt_compat::optarg;
+using ::perfetto::base::getopt_compat::opterr;
 using ::perfetto::base::getopt_compat::optind;
 using ::perfetto::base::getopt_compat::option;
+using ::perfetto::base::getopt_compat::optopt;
 constexpr auto getopt = ::perfetto::base::getopt_compat::getopt;
 constexpr auto getopt_long = ::perfetto::base::getopt_compat::getopt_long;
 constexpr auto no_argument = ::perfetto::base::getopt_compat::no_argument;
diff --git a/include/perfetto/ext/base/getopt_compat.h b/include/perfetto/ext/base/getopt_compat.h
index d1f5436..d854ff0 100644
--- a/include/perfetto/ext/base/getopt_compat.h
+++ b/include/perfetto/ext/base/getopt_compat.h
@@ -53,6 +53,8 @@
 
 extern char* optarg;
 extern int optind;
+extern int optopt;
+extern int opterr;
 
 int getopt_long(int argc,
                 char** argv,
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 20382a1..c26d949 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -326,7 +326,8 @@
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
       size_t shared_memory_page_size_hint_bytes = 0,
-      std::unique_ptr<SharedMemory> shm = nullptr) = 0;
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      const std::string& sdk_version = {}) = 0;
 
   // Connects a Consumer instance and obtains a ConsumerEndpoint, which is
   // essentially a 1:1 channel between one Consumer and the Service.
diff --git a/protos/perfetto/common/tracing_service_state.proto b/protos/perfetto/common/tracing_service_state.proto
index 119cea8..1ef4c56 100644
--- a/protos/perfetto/common/tracing_service_state.proto
+++ b/protos/perfetto/common/tracing_service_state.proto
@@ -34,6 +34,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -57,4 +63,10 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 4c6e882..c8b55e3 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -197,6 +197,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
 
 // End of protos/perfetto/common/tracing_service_state.proto
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index 1b193eb..9be72ea 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -152,6 +152,21 @@
   // SetupTracing response. See TracingService::ConnectProducer() and
   // |using_shmem_provided_by_producer| in InitializeConnectionResponse.
   optional bool producer_provided_shmem = 6;
+
+  // ---------------------------------------------------
+  // All fields below have been introduced in Android S.
+  // ---------------------------------------------------
+
+  // The version of the client library used by the producer.
+  // This is a human readable string with and its format varies depending on
+  // the build system that is used to build the code and the repo (standalone
+  // vs AOSP). This is intended for human debugging only.
+  optional string sdk_version = 8;
+
+  // On Windows, when producer_provided_shmem = true, the client creates a named
+  // SHM region and passes the name (an unguessable token) back to the service.
+  // Introduced in v13.
+  optional string shm_key_windows = 7;
 }
 
 message InitializeConnectionResponse {
@@ -262,9 +277,16 @@
 
   message StopDataSource { optional uint64 instance_id = 1; }
 
-  // This message also transports the file descriptor for the shared memory
-  // buffer (not a proto field).
-  message SetupTracing { optional uint32 shared_buffer_page_size_kb = 1; }
+  // On Android/Linux/Mac this message also transports the file descriptor for
+  // the shared memory buffer (not a proto field).
+  message SetupTracing {
+    optional uint32 shared_buffer_page_size_kb = 1;
+
+    // On Windows, instead, we pass the name (an unguessable token) of a shared
+    // memory region that can be attached by the other process by name.
+    // Introduced in v13.
+    optional string shm_key_windows = 2;
+  }
 
   message Flush {
     // The instance id (i.e. StartDataSource.new_instance_id) of the data
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index ed7b6ae..a807061 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -76,6 +76,7 @@
     "cpp",
     "lite",
     "zero",
+    "source_set",
   ]
   deps = [
     ":minimal_@TYPE@",
@@ -109,12 +110,11 @@
   sources = [ "perfetto_trace.proto" ]
 }
 
-if (perfetto_build_standalone) {
-  perfetto_proto_library("descriptor") {
-    proto_generators = [ "descriptor" ]
-    generate_descriptor = "trace.descriptor"
-    sources = [ "trace.proto" ]
-  }
+perfetto_proto_library("descriptor") {
+  proto_generators = [ "descriptor" ]
+  generate_descriptor = "trace.descriptor"
+  sources = [ "trace.proto" ]
+  deps = [ ":non_minimal_source_set" ]
 }
 
 # This target exports perfetto trace protos allowing both host and device
diff --git a/protos/perfetto/trace/ftrace/BUILD.gn b/protos/perfetto/trace/ftrace/BUILD.gn
index 5684450..7edf7fe 100644
--- a/protos/perfetto/trace/ftrace/BUILD.gn
+++ b/protos/perfetto/trace/ftrace/BUILD.gn
@@ -21,6 +21,7 @@
     "cpp",
     "zero",
     "lite",
+    "source_set",
   ]
   sources = ftrace_proto_names
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 57f945c..7fe24f3 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -197,6 +197,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
 
 // End of protos/perfetto/common/tracing_service_state.proto
@@ -8326,6 +8338,12 @@
 
   // Ticks per second - sysconf(_SC_CLK_TCK).
   optional int64 hz = 3;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 4;
 }
 
 // End of protos/perfetto/trace/system_info.proto
diff --git a/protos/perfetto/trace/system_info.proto b/protos/perfetto/trace/system_info.proto
index 90a7a57..ddce4ca 100644
--- a/protos/perfetto/trace/system_info.proto
+++ b/protos/perfetto/trace/system_info.proto
@@ -31,4 +31,10 @@
 
   // Ticks per second - sysconf(_SC_CLK_TCK).
   optional int64 hz = 3;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 4;
 }
diff --git a/src/base/getopt_compat.cc b/src/base/getopt_compat.cc
index 19c8adc..0c80dc6 100644
--- a/src/base/getopt_compat.cc
+++ b/src/base/getopt_compat.cc
@@ -30,6 +30,8 @@
 
 char* optarg = nullptr;
 int optind = 0;
+int optopt = 0;
+int opterr = 1;
 
 namespace {
 
@@ -47,7 +49,7 @@
 
 const option* LookupShortOpt(const std::vector<option>& opts, char c) {
   for (const option& opt : opts) {
-    if (opt.name == nullptr && opt.val == c)
+    if (!*opt.name && opt.val == c)
       return &opt;
   }
   return nullptr;
@@ -78,6 +80,7 @@
     }
     res->emplace_back();
     option& opt = res->back();
+    opt.name = "";
     opt.val = c;
     opt.has_arg = no_argument;
     if (*sopt == ':') {
@@ -108,6 +111,7 @@
     return '?';
 
   char* arg = argv[optind];
+  optopt = 0;
 
   if (!nextchar) {
     // If |nextchar| is null we are NOT in the middle of a short option and we
@@ -120,11 +124,14 @@
 
       size_t len = sep ? static_cast<size_t>(sep - arg) : strlen(arg);
       const option* opt = LookupLongOpt(opts, arg, len);
+
       if (!opt) {
-        fprintf(stderr, "unrecognized option '--%s'\n", arg);
+        if (opterr)
+          fprintf(stderr, "unrecognized option '--%s'\n", arg);
         return '?';
       }
 
+      optopt = opt->val;
       if (opt->has_arg == no_argument) {
         if (sep) {
           fprintf(stderr, "option '--%s' doesn't allow an argument\n", arg);
@@ -137,7 +144,8 @@
           optarg = sep + 1;
           return opt->val;
         } else if (optind >= argc) {
-          fprintf(stderr, "option '--%s' requires an argument\n", arg);
+          if (opterr)
+            fprintf(stderr, "option '--%s' requires an argument\n", arg);
           return '?';
         } else {
           optarg = argv[optind++];
@@ -173,8 +181,10 @@
     }
 
     const option* opt = LookupShortOpt(opts, cur_char);
+    optopt = cur_char;
     if (!opt) {
-      fprintf(stderr, "invalid option -- '%c'\n", cur_char);
+      if (opterr)
+        fprintf(stderr, "invalid option -- '%c'\n", cur_char);
       return '?';
     }
     if (opt->has_arg == no_argument) {
@@ -189,7 +199,8 @@
       if (!nextchar) {
         // Case 1.
         if (optind >= argc) {
-          fprintf(stderr, "option requires an argument -- '%c'\n", cur_char);
+          if (opterr)
+            fprintf(stderr, "option requires an argument -- '%c'\n", cur_char);
           return '?';
         } else {
           optarg = argv[optind++];
diff --git a/src/base/getopt_compat_unittest.cc b/src/base/getopt_compat_unittest.cc
index a7d4db3..aeeb2ed 100644
--- a/src/base/getopt_compat_unittest.cc
+++ b/src/base/getopt_compat_unittest.cc
@@ -47,6 +47,8 @@
   GetoptFn getopt = &getopt_compat::getopt;
   GetoptLongFn getopt_long = &getopt_compat::getopt_long;
   int& optind = getopt_compat::optind;
+  int& optopt = getopt_compat::optopt;
+  int& opterr = getopt_compat::opterr;
   char*& optarg = getopt_compat::optarg;
 };
 
@@ -58,6 +60,8 @@
   GetoptFn getopt = &::getopt;
   GetoptLongFn getopt_long = &::getopt_long;
   int& optind = ::optind;
+  int& optopt = ::optopt;
+  int& opterr = ::opterr;
   char*& optarg = ::optarg;
 };
 #endif
@@ -382,6 +386,33 @@
   }
 }
 
+TYPED_TEST(GetoptCompatTest, OpterrHandling) {
+  auto& t = this->impl;
+  t.opterr = 0;  // Make errors silent.
+
+  const char* sops = "ab:";
+  this->SetCmdline({"argv0", "-a", "-c", "-b"});
+  EXPECT_EQ(t.getopt(this->argc, this->argv, sops), 'a');
+  EXPECT_EQ(t.optarg, nullptr);
+  EXPECT_EQ(t.getopt(this->argc, this->argv, sops), '?');
+  EXPECT_EQ(t.optopt, 'c');
+  EXPECT_EQ(t.getopt(this->argc, this->argv, sops), '?');
+  EXPECT_EQ(t.optopt, 'b');
+  EXPECT_EQ(t.getopt(this->argc, this->argv, sops), -1);
+
+  using LongOptionType = typename decltype(this->impl)::LongOptionType;
+  LongOptionType lopts[]{
+      {"requires_arg", 1 /*required_argument*/, nullptr, 42},
+      {nullptr, 0, nullptr, 0},
+  };
+  this->SetCmdline({"argv0", "-a", "--unkonwn", "--requires_arg"});
+  EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), 'a');
+  EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), '?');
+  EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), '?');
+  EXPECT_EQ(t.optopt, 42);
+  EXPECT_EQ(t.getopt_long(this->argc, this->argv, sops, lopts, nullptr), -1);
+}
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index c8f6abc..13cf84a 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -1037,6 +1037,7 @@
     printf("  id: %d\n", producer.id());
     printf("  name: \"%s\" \n", producer.name().c_str());
     printf("  uid: %d \n", producer.uid());
+    printf("  sdk_version: \"%s\" \n", producer.sdk_version().c_str());
     printf("}\n");
   }
 
@@ -1048,6 +1049,8 @@
     printf("  }\n");
     printf("}\n");
   }
+  printf("tracing_service_version: \"%s\"\n",
+         svc_state.tracing_service_version().c_str());
   printf("num_sessions: %d\n", svc_state.num_sessions());
   printf("num_sessions_started: %d\n", svc_state.num_sessions_started());
 }
diff --git a/src/profiling/memory/client_api_factory_standalone.cc b/src/profiling/memory/client_api_factory_standalone.cc
index f21ce0d..b49da1f 100644
--- a/src/profiling/memory/client_api_factory_standalone.cc
+++ b/src/profiling/memory/client_api_factory_standalone.cc
@@ -149,6 +149,9 @@
         }
       });
   task_runner.Run();
+  // We currently do not Quit the task_runner, but if we ever do it will be
+  // very hard to debug if we don't exit here.
+  exit(0);
 }
 
 // This is called by AHeapProfile_initSession (client_api.cc) to construct a
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index c460426..6a47344 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -573,7 +573,7 @@
     return;
   }
 
-  PERFETTO_DLOG("Stopping data source %" PRIu64, id);
+  PERFETTO_LOG("Stopping data source %" PRIu64, id);
 
   DataSource& data_source = it->second;
   data_source.was_stopped = true;
@@ -1211,6 +1211,8 @@
     DataSource& ds = p.second;
     if (gr.IsOverCpuThreshold(ds.guardrail_config)) {
       ds.hit_guardrail = true;
+      PERFETTO_LOG("Data source %" PRIu64 " hit CPU guardrail. Shutting down.",
+                   ds.id);
       ShutdownDataSource(&ds);
     }
   }
@@ -1230,6 +1232,9 @@
     DataSource& ds = p.second;
     if (gr.IsOverMemoryThreshold(ds.guardrail_config)) {
       ds.hit_guardrail = true;
+      PERFETTO_LOG("Data source %" PRIu64
+                   " hit memory guardrail. Shutting down.",
+                   ds.id);
       ShutdownDataSource(&ds);
     }
   }
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index 69d911a..b11a569 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -44,9 +44,22 @@
   ]
 }
 
+source_set("proto_ring_buffer") {
+  deps = [
+    ":protozero",
+    "../../gn:default_deps",
+    "../base",
+  ]
+  sources = [
+    "proto_ring_buffer.cc",
+    "proto_ring_buffer.h",
+  ]
+}
+
 perfetto_unittest_source_set("unittests") {
   testonly = true
   deps = [
+    ":proto_ring_buffer",
     ":protozero",
     ":testing_messages_cpp",
     ":testing_messages_lite",
@@ -62,6 +75,7 @@
     "message_handle_unittest.cc",
     "message_unittest.cc",
     "proto_decoder_unittest.cc",
+    "proto_ring_buffer_unittest.cc",
     "proto_utils_unittest.cc",
     "scattered_stream_writer_unittest.cc",
     "test/cppgen_conformance_unittest.cc",
diff --git a/src/trace_processor/rpc/proto_ring_buffer.cc b/src/protozero/proto_ring_buffer.cc
similarity index 95%
rename from src/trace_processor/rpc/proto_ring_buffer.cc
rename to src/protozero/proto_ring_buffer.cc
index 3354efe..92fe153 100644
--- a/src/trace_processor/rpc/proto_ring_buffer.cc
+++ b/src/protozero/proto_ring_buffer.cc
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/rpc/proto_ring_buffer.h"
+#include "src/protozero/proto_ring_buffer.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/protozero/proto_utils.h"
 
-namespace perfetto {
-namespace trace_processor {
+namespace protozero {
 
 namespace {
 constexpr size_t kGrowBytes = 128 * 1024;
@@ -74,7 +73,7 @@
 }  // namespace
 
 ProtoRingBuffer::ProtoRingBuffer()
-    : buf_(base::PagedMemory::Allocate(kGrowBytes)) {}
+    : buf_(perfetto::base::PagedMemory::Allocate(kGrowBytes)) {}
 ProtoRingBuffer::~ProtoRingBuffer() = default;
 
 void ProtoRingBuffer::Append(const void* data_void, size_t data_len) {
@@ -137,7 +136,7 @@
         failed_ = true;
         return;
       }
-      auto new_buf = base::PagedMemory::Allocate(new_size);
+      auto new_buf = perfetto::base::PagedMemory::Allocate(new_size);
       memcpy(new_buf.Get(), buf_.Get(), buf_.size());
       buf_ = std::move(new_buf);
       avail = new_size - wr_;
@@ -184,5 +183,4 @@
   return msg;
 }
 
-}  // namespace trace_processor
-}  // namespace perfetto
+}  // namespace protozero
diff --git a/src/trace_processor/rpc/proto_ring_buffer.h b/src/protozero/proto_ring_buffer.h
similarity index 85%
rename from src/trace_processor/rpc/proto_ring_buffer.h
rename to src/protozero/proto_ring_buffer.h
index 62934ca..06ef539 100644
--- a/src/trace_processor/rpc/proto_ring_buffer.h
+++ b/src/protozero/proto_ring_buffer.h
@@ -14,28 +14,28 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_RPC_PROTO_RING_BUFFER_H_
-#define SRC_TRACE_PROCESSOR_RPC_PROTO_RING_BUFFER_H_
+#ifndef SRC_PROTOZERO_PROTO_RING_BUFFER_H_
+#define SRC_PROTOZERO_PROTO_RING_BUFFER_H_
 
 #include <stdint.h>
 
 #include "perfetto/ext/base/paged_memory.h"
 
-namespace perfetto {
-namespace trace_processor {
+namespace protozero {
 
-// This class buffers and tokenizes proto messages used for the TraceProcessor
-// RPC interface (See comments in trace_processor.proto).
-// From a logical level, the RPC is a sequence of protos like this.
+// This class buffers and tokenizes proto messages.
+//
+// From a logical level, it works with a sequence of protos like this.
 // [ header 1 ] [ payload 1   ]
 // [ header 2 ] [ payload 2  ]
 // [ header 3 ] [ payload 3     ]
 // Where [ header ] is a variable-length sequence of:
 // [ Field ID = 1, type = length-delimited] [ length (varint) ].
-// The RPC pipe is byte-oriented, not message-oriented (like a TCP stream).
-// The pipe is not required to respect the boundaries of each message, it only
-// guarantees that data is not lost or duplicated. The following sequence of
-// inbound events is possible:
+//
+// The input to this class is byte-oriented, not message-oriented (like a TCP
+// stream or a pipe). The caller is not required to respect the boundaries of
+// each message; only guarantee that data is not lost or duplicated. The
+// following sequence of inbound events is possible:
 // 1. [ hdr 1 (incomplete) ... ]
 // 2. [ ... hdr 1 ] [ payload 1 ] [ hdr 2 ] [ payoad 2 ] [ hdr 3 ] [ pay... ]
 // 3. [ ...load 3 ]
@@ -126,14 +126,13 @@
   size_t avail() const { return buf_.size() - (wr_ - rd_); }
 
  private:
-  base::PagedMemory buf_;
+  perfetto::base::PagedMemory buf_;
   Message fastpath_{};
   bool failed_ = false;  // Set in case of an unrecoverable framing faiulre.
   size_t rd_ = 0;        // Offset of the read cursor in |buf_|.
   size_t wr_ = 0;        // Offset of the write cursor in |buf_|.
 };
 
-}  // namespace trace_processor
-}  // namespace perfetto
+}  // namespace protozero
 
-#endif  // SRC_TRACE_PROCESSOR_RPC_PROTO_RING_BUFFER_H_
+#endif  // SRC_PROTOZERO_PROTO_RING_BUFFER_H_
diff --git a/src/trace_processor/rpc/proto_ring_buffer_unittest.cc b/src/protozero/proto_ring_buffer_unittest.cc
similarity index 95%
rename from src/trace_processor/rpc/proto_ring_buffer_unittest.cc
rename to src/protozero/proto_ring_buffer_unittest.cc
index 0e3944c..263baf3 100644
--- a/src/trace_processor/rpc/proto_ring_buffer_unittest.cc
+++ b/src/protozero/proto_ring_buffer_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/rpc/proto_ring_buffer.h"
+#include "src/protozero/proto_ring_buffer.h"
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -30,8 +30,7 @@
 
 using testing::ElementsAre;
 
-namespace perfetto {
-namespace trace_processor {
+namespace protozero {
 
 // For ASSERT_EQ()
 inline bool operator==(const ProtoRingBuffer::Message& a,
@@ -58,6 +57,8 @@
 
 namespace {
 
+using ::perfetto::base::ArraySize;
+
 constexpr uint32_t kMaxMsgSize = ProtoRingBuffer::kMaxMsgSize;
 
 class ProtoRingBufferTest : public ::testing::Test {
@@ -137,14 +138,14 @@
 
   uint32_t frag_lens[] = {120, 20, 471, 1};
   uint32_t frag_sum = 0;
-  for (uint32_t i = 0; i < base::ArraySize(frag_lens); i++)
+  for (uint32_t i = 0; i < ArraySize(frag_lens); i++)
     frag_sum += frag_lens[i];
   ASSERT_EQ(frag_sum, last_msg_.size());
 
   // Append the messages in such a way that each appen either passes a portion
   // of a message (the 20 ones) or more than a message.
   uint32_t written = 0;
-  for (uint32_t i = 0; i < base::ArraySize(frag_lens); i++) {
+  for (uint32_t i = 0; i < ArraySize(frag_lens); i++) {
     buf.Append(&last_msg_[written], frag_lens[i]);
     written += frag_lens[i];
     for (;;) {
@@ -228,5 +229,4 @@
 }
 
 }  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
+}  // namespace protozero
diff --git a/src/trace_processor/importers/json/json_trace_parser.cc b/src/trace_processor/importers/json/json_trace_parser.cc
index eabd57d..85de296 100644
--- a/src/trace_processor/importers/json/json_trace_parser.cc
+++ b/src/trace_processor/importers/json/json_trace_parser.cc
@@ -25,6 +25,7 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/flow_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
@@ -148,6 +149,61 @@
       MaybeAddFlow(track_id, value);
       break;
     }
+    case 'C': {  // TRACE_EVENT_COUNTER
+      auto args = value["args"];
+      if (!args.isObject()) {
+        context_->storage->IncrementStats(stats::json_parser_failure);
+        break;
+      }
+
+      std::string counter_name_prefix = name.ToStdString();
+      if (value.isMember("id")) {
+        counter_name_prefix += " id: " + value["id"].asString();
+      }
+
+      for (auto it = args.begin(); it != args.end(); ++it) {
+        std::string counter_name = counter_name_prefix + " " + it.name();
+        StringId counter_name_id =
+            context_->storage->InternString(base::StringView(counter_name));
+        TrackId track_id = context_->track_tracker->InternProcessCounterTrack(
+            counter_name_id, utid);
+        context_->event_tracker->PushCounter(timestamp, it->asDouble(),
+                                             track_id);
+      }
+      break;
+    }
+    case 'i': {  // TRACE_EVENT_INSTANT
+      base::StringView scope;
+      if (value.isMember("s")) {
+        scope = value["s"].asCString();
+      }
+
+      TrackId track_id;
+      if (scope == "g") {
+        track_id = context_->track_tracker
+                       ->GetOrCreateLegacyChromeGlobalInstantTrack();
+      } else if (scope == "p") {
+        if (!opt_pid) {
+          context_->storage->IncrementStats(stats::json_parser_failure);
+          break;
+        }
+        UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
+        track_id =
+            context_->track_tracker->InternLegacyChromeProcessInstantTrack(
+                upid);
+      } else if (scope == "t" || scope.data() == nullptr) {
+        if (!opt_tid) {
+          context_->storage->IncrementStats(stats::json_parser_failure);
+          break;
+        }
+        track_id = context_->track_tracker->InternThreadTrack(utid);
+      } else {
+        context_->storage->IncrementStats(stats::json_parser_failure);
+        break;
+      }
+      context_->slice_tracker->Scoped(timestamp, track_id, cat_id, name_id, 0);
+      break;
+    }
     case 's': {  // TRACE_EVENT_FLOW_START
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
       auto opt_source_id =
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index fe2492c..4f34d3c 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -71,8 +71,9 @@
 
 }  // namespace
 
-ProtoBuilder::ProtoBuilder(const ProtoDescriptor* descriptor)
-    : descriptor_(descriptor) {}
+ProtoBuilder::ProtoBuilder(const DescriptorPool* pool,
+                           const ProtoDescriptor* descriptor)
+    : pool_(pool), descriptor_(descriptor) {}
 
 util::Status ProtoBuilder::AppendSqlValue(const std::string& field_name,
                                           const SqlValue& value) {
@@ -117,9 +118,32 @@
     case FieldDescriptorProto::TYPE_INT64:
     case FieldDescriptorProto::TYPE_UINT32:
     case FieldDescriptorProto::TYPE_BOOL:
-    case FieldDescriptorProto::TYPE_ENUM:
       message_->AppendVarInt(field->number(), value);
       break;
+    case FieldDescriptorProto::TYPE_ENUM: {
+      auto opt_enum_descriptor_idx =
+          pool_->FindDescriptorIdx(field->resolved_type_name());
+      if (!opt_enum_descriptor_idx) {
+        return util::ErrStatus(
+            "Unable to find enum type %s to fill field %s (in proto message "
+            "%s)",
+            field->resolved_type_name().c_str(), field->name().c_str(),
+            descriptor_->full_name().c_str());
+      }
+      const auto& enum_desc = pool_->descriptors()[*opt_enum_descriptor_idx];
+      auto opt_enum_str = enum_desc.FindEnumString(static_cast<int32_t>(value));
+      if (!opt_enum_str) {
+        return util::ErrStatus("Invalid enum value %" PRId64
+                               " "
+                               "in enum type %s; encountered while filling "
+                               "field %s (in proto message %s)",
+                               value, field->resolved_type_name().c_str(),
+                               field->name().c_str(),
+                               descriptor_->full_name().c_str());
+      }
+      message_->AppendVarInt(field->number(), value);
+      break;
+    }
     case FieldDescriptorProto::TYPE_SINT32:
     case FieldDescriptorProto::TYPE_SINT64:
       message_->AppendSignedVarInt(field->number(), value);
@@ -207,6 +231,30 @@
       message_->AppendBytes(field->number(), data.data(), data.size());
       break;
     }
+    case FieldDescriptorProto::TYPE_ENUM: {
+      auto opt_enum_descriptor_idx =
+          pool_->FindDescriptorIdx(field->resolved_type_name());
+      if (!opt_enum_descriptor_idx) {
+        return util::ErrStatus(
+            "Unable to find enum type %s to fill field %s (in proto message "
+            "%s)",
+            field->resolved_type_name().c_str(), field->name().c_str(),
+            descriptor_->full_name().c_str());
+      }
+      const auto& enum_desc = pool_->descriptors()[*opt_enum_descriptor_idx];
+      std::string enum_str = data.ToStdString();
+      auto opt_enum_value = enum_desc.FindEnumValue(enum_str);
+      if (!opt_enum_value) {
+        return util::ErrStatus(
+            "Invalid enum string %s "
+            "in enum type %s; encountered while filling "
+            "field %s (in proto message %s)",
+            enum_str.c_str(), field->resolved_type_name().c_str(),
+            field->name().c_str(), descriptor_->full_name().c_str());
+      }
+      message_->AppendVarInt(field->number(), *opt_enum_value);
+      break;
+    }
     default: {
       return util::ErrStatus(
           "Tried to write value of type string into field %s (in proto type "
@@ -546,7 +594,7 @@
     return;
   }
 
-  ProtoBuilder builder(fn_ctx->desc);
+  ProtoBuilder builder(fn_ctx->pool, fn_ctx->desc);
   for (int i = 0; i < argc; i += 2) {
     if (sqlite3_value_type(argv[i]) != SQLITE_TEXT) {
       sqlite3_result_error(ctx, "BuildProto: Invalid args", -1);
@@ -637,9 +685,10 @@
 util::Status ComputeMetrics(TraceProcessor* tp,
                             const std::vector<std::string> metrics_to_compute,
                             const std::vector<SqlMetricFile>& sql_metrics,
+                            const DescriptorPool& pool,
                             const ProtoDescriptor& root_descriptor,
                             std::vector<uint8_t>* metrics_proto) {
-  ProtoBuilder metric_builder(&root_descriptor);
+  ProtoBuilder metric_builder(&pool, &root_descriptor);
   for (const auto& name : metrics_to_compute) {
     auto metric_it =
         std::find_if(sql_metrics.begin(), sql_metrics.end(),
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index dea62f7..b999225 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -61,7 +61,7 @@
 // Visible for testing.
 class ProtoBuilder {
  public:
-  ProtoBuilder(const ProtoDescriptor*);
+  ProtoBuilder(const DescriptorPool*, const ProtoDescriptor*);
 
   util::Status AppendSqlValue(const std::string& field_name,
                               const SqlValue& value);
@@ -106,6 +106,7 @@
                               const uint8_t* ptr,
                               size_t size);
 
+  const DescriptorPool* pool_ = nullptr;
   const ProtoDescriptor* descriptor_ = nullptr;
   protozero::HeapBuffered<protozero::Message> message_;
 };
@@ -176,6 +177,7 @@
 util::Status ComputeMetrics(TraceProcessor* impl,
                             const std::vector<std::string> metrics_to_compute,
                             const std::vector<SqlMetricFile>& metrics,
+                            const DescriptorPool& pool,
                             const ProtoDescriptor& root_descriptor,
                             std::vector<uint8_t>* metrics_proto);
 
diff --git a/src/trace_processor/metrics/metrics_unittest.cc b/src/trace_processor/metrics/metrics_unittest.cc
index 661df33..29f8f6d 100644
--- a/src/trace_processor/metrics/metrics_unittest.cc
+++ b/src/trace_processor/metrics/metrics_unittest.cc
@@ -74,13 +74,14 @@
   // message TestProto {
   //   optional int64 int_value = 1;
   // }
+  DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
                              ProtoDescriptor::Type::kMessage, base::nullopt);
   descriptor.AddField(FieldDescriptor(
       "int_value", 1, FieldDescriptorProto::TYPE_INT64, "", false));
 
-  ProtoBuilder builder(&descriptor);
+  ProtoBuilder builder(&pool, &descriptor);
   ASSERT_TRUE(builder.AppendLong("int_value", 12345).ok());
 
   auto result_ser = builder.SerializeToProtoBuilderResult();
@@ -96,13 +97,14 @@
   // message TestProto {
   //   optional double double_value = 1;
   // }
+  DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
                              ProtoDescriptor::Type::kMessage, base::nullopt);
   descriptor.AddField(FieldDescriptor(
       "double_value", 1, FieldDescriptorProto::TYPE_DOUBLE, "", false));
 
-  ProtoBuilder builder(&descriptor);
+  ProtoBuilder builder(&pool, &descriptor);
   ASSERT_TRUE(builder.AppendDouble("double_value", 1.2345).ok());
 
   auto result_ser = builder.SerializeToProtoBuilderResult();
@@ -118,13 +120,14 @@
   // message TestProto {
   //   optional string string_value = 1;
   // }
+  DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
                              ProtoDescriptor::Type::kMessage, base::nullopt);
   descriptor.AddField(FieldDescriptor(
       "string_value", 1, FieldDescriptorProto::TYPE_STRING, "", false));
 
-  ProtoBuilder builder(&descriptor);
+  ProtoBuilder builder(&pool, &descriptor);
   ASSERT_TRUE(builder.AppendString("string_value", "hello world!").ok());
 
   auto result_ser = builder.SerializeToProtoBuilderResult();
@@ -143,6 +146,7 @@
   //   }
   //   optional NestedProto nested_value = 1;
   // }
+  DescriptorPool pool;
   ProtoDescriptor nested("file.proto", ".perfetto.protos",
                          ".perfetto.protos.TestProto.NestedProto",
                          ProtoDescriptor::Type::kMessage, base::nullopt);
@@ -158,12 +162,12 @@
   field.set_resolved_type_name(".perfetto.protos.TestProto.NestedProto");
   descriptor.AddField(field);
 
-  ProtoBuilder nest_builder(&nested);
+  ProtoBuilder nest_builder(&pool, &nested);
   ASSERT_TRUE(nest_builder.AppendLong("nested_int_value", 789).ok());
 
   auto nest_ser = nest_builder.SerializeToProtoBuilderResult();
 
-  ProtoBuilder builder(&descriptor);
+  ProtoBuilder builder(&pool, &descriptor);
   ASSERT_TRUE(
       builder.AppendBytes("nested_value", nest_ser.data(), nest_ser.size())
           .ok());
@@ -190,6 +194,7 @@
   // message TestProto {
   //   repeated int64 int_value = 1;
   // }
+  DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
                              ProtoDescriptor::Type::kMessage, base::nullopt);
@@ -202,7 +207,7 @@
 
   std::vector<uint8_t> rep_ser = rep_builder.SerializeToProtoBuilderResult();
 
-  ProtoBuilder builder(&descriptor);
+  ProtoBuilder builder(&pool, &descriptor);
   ASSERT_TRUE(
       builder.AppendBytes("rep_int_value", rep_ser.data(), rep_ser.size())
           .ok());
@@ -215,6 +220,55 @@
   ASSERT_FALSE(++it);
 }
 
+TEST_F(ProtoBuilderTest, AppendEnums) {
+  using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
+
+  // Create the descriptor version of the following enum and message:
+  // enum TestEnum {
+  //   FIRST = 1,
+  //   SECOND = 2,
+  //   THIRD = 3
+  // }
+  // message TestMessage {
+  //   optional TestEnum enum_value = 1;
+  // }
+  DescriptorPool pool;
+  ProtoDescriptor enum_descriptor("file.proto", ".perfetto.protos",
+                                  ".perfetto.protos.TestEnum",
+                                  ProtoDescriptor::Type::kEnum, base::nullopt);
+  enum_descriptor.AddEnumValue(1, "FIRST");
+  enum_descriptor.AddEnumValue(2, "SECOND");
+  enum_descriptor.AddEnumValue(3, "THIRD");
+  pool.AddProtoDescriptorForTesting(enum_descriptor);
+
+  ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
+                             ".perfetto.protos.TestMessage",
+                             ProtoDescriptor::Type::kMessage, base::nullopt);
+  FieldDescriptor enum_field("enum_value", 1, FieldDescriptorProto::TYPE_ENUM,
+                             ".perfetto.protos.TestEnum", false);
+  enum_field.set_resolved_type_name(".perfetto.protos.TestEnum");
+  descriptor.AddField(enum_field);
+  pool.AddProtoDescriptorForTesting(descriptor);
+
+  ProtoBuilder value_builder(&pool, &descriptor);
+  ASSERT_FALSE(value_builder.AppendLong("enum_value", 4).ok());
+  ASSERT_TRUE(value_builder.AppendLong("enum_value", 3).ok());
+  ASSERT_FALSE(value_builder.AppendLong("enum_value", 6).ok());
+
+  auto value_proto = DecodeSingleFieldProto<false>(
+      value_builder.SerializeToProtoBuilderResult());
+  ASSERT_EQ(value_proto.Get(1).as_int32(), 3);
+
+  ProtoBuilder str_builder(&pool, &descriptor);
+  ASSERT_FALSE(str_builder.AppendString("enum_value", "FOURTH").ok());
+  ASSERT_TRUE(str_builder.AppendString("enum_value", "SECOND").ok());
+  ASSERT_FALSE(str_builder.AppendString("enum_value", "OTHER").ok());
+
+  auto str_proto = DecodeSingleFieldProto<false>(
+      str_builder.SerializeToProtoBuilderResult());
+  ASSERT_EQ(str_proto.Get(1).as_int32(), 2);
+}
+
 }  // namespace
 
 }  // namespace metrics
diff --git a/src/trace_processor/metrics/trace_stats.sql b/src/trace_processor/metrics/trace_stats.sql
index f647442..702fc91 100644
--- a/src/trace_processor/metrics/trace_stats.sql
+++ b/src/trace_processor/metrics/trace_stats.sql
@@ -22,16 +22,16 @@
       'name', name,
       'idx', idx,
       'count', value,
-      -- TraceAnalysisStats.Source enum:
       'source', CASE source
-        WHEN 'trace' THEN 1
-        WHEN 'analysis' THEN 2
+        WHEN 'trace' THEN 'SOURCE_TRACE'
+        WHEN 'analysis' THEN 'SOURCE_ANALYSIS'
+        ELSE 'SOURCE_UNKNOWN'
       END,
-      -- TraceAnalysisStats.Severity enum:
       'severity', CASE severity
-        WHEN 'info' THEN 1
-        WHEN 'data_loss' THEN 2
-        WHEN 'error' THEN 3
+        WHEN 'info' THEN 'SEVERITY_INFO'
+        WHEN 'data_loss' THEN 'SEVERITY_DATA_LOSS'
+        WHEN 'error' THEN 'SEVERITY_ERROR'
+        ELSE 'SEVERITY_UNKNOWN'
       END
     ))
     FROM stats ORDER BY name ASC
diff --git a/src/trace_processor/rpc/BUILD.gn b/src/trace_processor/rpc/BUILD.gn
index 63cc752..09b9061 100644
--- a/src/trace_processor/rpc/BUILD.gn
+++ b/src/trace_processor/rpc/BUILD.gn
@@ -23,8 +23,6 @@
 # interface) and by the :httpd module for the HTTP interface.
 source_set("rpc") {
   sources = [
-    "proto_ring_buffer.cc",
-    "proto_ring_buffer.h",
     "query_result_serializer.cc",
     "query_result_serializer.h",
     "rpc.cc",
@@ -38,15 +36,13 @@
     "../../../protos/perfetto/trace_processor:zero",
     "../../base",
     "../../protozero",
+    "../../protozero:proto_ring_buffer",
   ]
 }
 
 perfetto_unittest_source_set("unittests") {
   testonly = true
-  sources = [
-    "proto_ring_buffer_unittest.cc",
-    "query_result_serializer_unittest.cc",
-  ]
+  sources = [ "query_result_serializer_unittest.cc" ]
   deps = [
     ":rpc",
     "..:lib",
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 68def35..5418f55 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -1033,7 +1033,7 @@
     return util::Status("Root metrics proto descriptor not found");
 
   const auto& root_descriptor = pool_.descriptors()[opt_idx.value()];
-  return metrics::ComputeMetrics(this, metric_names, sql_metrics_,
+  return metrics::ComputeMetrics(this, metric_names, sql_metrics_, pool_,
                                  root_descriptor, metrics_proto);
 }
 
diff --git a/src/trace_processor/trace_sorter.cc b/src/trace_processor/trace_sorter.cc
index 760bf75..c1bc9e5 100644
--- a/src/trace_processor/trace_sorter.cc
+++ b/src/trace_processor/trace_sorter.cc
@@ -185,13 +185,14 @@
   if (PERFETTO_UNLIKELY(bypass_next_stage_for_testing_))
     return;
 
+  int64_t timestamp = ttp.timestamp;
   if (queue_idx == 0) {
     // queues_[0] is for non-ftrace packets.
-    parser_->ParseTracePacket(ttp.timestamp, std::move(ttp));
+    parser_->ParseTracePacket(timestamp, std::move(ttp));
   } else {
     // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
     uint32_t cpu = static_cast<uint32_t>(queue_idx - 1);
-    parser_->ParseFtracePacket(cpu, ttp.timestamp, std::move(ttp));
+    parser_->ParseFtracePacket(cpu, timestamp, std::move(ttp));
   }
 }
 
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
index abcf01a..a93cd26 100644
--- a/src/trace_processor/trace_sorter.h
+++ b/src/trace_processor/trace_sorter.h
@@ -91,8 +91,10 @@
 
   inline void PushSystraceLine(std::unique_ptr<SystraceLine> systrace_line) {
     DCHECK_ftrace_batch_cpu(kNoBatch);
+
+    int64_t timestamp = systrace_line->ts;
     AppendNonFtraceAndMaybeExtractEvents(TimestampedTracePiece(
-        systrace_line->ts, packet_idx_++, std::move(systrace_line)));
+        timestamp, packet_idx_++, std::move(systrace_line)));
   }
 
   inline void PushTrackEventPacket(int64_t timestamp,
@@ -115,20 +117,20 @@
     // global ordering and doing that in batches only after all ftrace events
     // for a bundle are pushed.
   }
-
-  // As with |PushFtraceEvent|, doesn't immediately sort the affected queues.
-  // TODO(rsavitski): if a trace has a mix of normal & "compact" events (being
-  // pushed through this function), the ftrace batches will no longer be fully
-  // sorted by timestamp. In such situations, we will have to sort at the end of
-  // the batch. We can do better as both sub-sequences are sorted however.
-  // Consider adding extra queues, or pushing them in a merge-sort fashion
-  // instead.
   inline void PushInlineFtraceEvent(uint32_t cpu,
                                     int64_t timestamp,
                                     InlineSchedSwitch inline_sched_switch) {
     set_ftrace_batch_cpu_for_DCHECK(cpu);
     GetQueue(cpu + 1)->Append(
         TimestampedTracePiece(timestamp, packet_idx_++, inline_sched_switch));
+
+    // As with |PushFtraceEvent|, doesn't immediately sort the affected queues.
+    // TODO(rsavitski): if a trace has a mix of normal & "compact" events (being
+    // pushed through this function), the ftrace batches will no longer be fully
+    // sorted by timestamp. In such situations, we will have to sort at the end
+    // of the batch. We can do better as both sub-sequences are sorted however.
+    // Consider adding extra queues, or pushing them in a merge-sort fashion
+    // instead.
   }
   inline void PushInlineFtraceEvent(uint32_t cpu,
                                     int64_t timestamp,
diff --git a/src/trace_processor/util/descriptors.cc b/src/trace_processor/util/descriptors.cc
index b5b00e1..068da6f 100644
--- a/src/trace_processor/util/descriptors.cc
+++ b/src/trace_processor/util/descriptors.cc
@@ -148,8 +148,8 @@
 
   auto idx = static_cast<uint32_t>(descriptors_.size()) - 1;
   for (auto it = decoder.enum_type(); it; ++it) {
-    AddEnumProtoDescriptors(file_name, package_name, idx, *it,
-                            merge_existing_messages);
+    RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package_name, idx, *it,
+                                            merge_existing_messages));
   }
   for (auto it = decoder.nested_type(); it; ++it) {
     RETURN_IF_ERROR(AddNestedProtoDescriptors(file_name, package_name, idx, *it,
@@ -229,8 +229,9 @@
           merge_existing_messages));
     }
     for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
-      AddEnumProtoDescriptors(file_name, package, base::nullopt, *enum_it,
-                              merge_existing_messages);
+      RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package, base::nullopt,
+                                              *enum_it,
+                                              merge_existing_messages));
     }
     for (auto ext_it = file.extension(); ext_it; ++ext_it) {
       extensions.emplace_back(package, *ext_it);
diff --git a/src/trace_processor/util/descriptors.h b/src/trace_processor/util/descriptors.h
index 7108781..a5c7cde 100644
--- a/src/trace_processor/util/descriptors.h
+++ b/src/trace_processor/util/descriptors.h
@@ -87,7 +87,9 @@
   void AddEnumValue(int32_t integer_representation,
                     std::string string_representation) {
     PERFETTO_DCHECK(type_ == Type::kEnum);
-    enum_values_[integer_representation] = std::move(string_representation);
+    enum_values_by_name_[string_representation] = integer_representation;
+    enum_names_by_value_[integer_representation] =
+        std::move(string_representation);
   }
 
   const FieldDescriptor* FindFieldByName(const std::string& name) const {
@@ -114,9 +116,16 @@
 
   base::Optional<std::string> FindEnumString(const int32_t value) const {
     PERFETTO_DCHECK(type_ == Type::kEnum);
-    auto it = enum_values_.find(value);
-    return it == enum_values_.end() ? base::nullopt
-                                    : base::Optional<std::string>(it->second);
+    auto it = enum_names_by_value_.find(value);
+    return it == enum_names_by_value_.end() ? base::nullopt
+                                            : base::make_optional(it->second);
+  }
+
+  base::Optional<int32_t> FindEnumValue(const std::string& value) const {
+    PERFETTO_DCHECK(type_ == Type::kEnum);
+    auto it = enum_values_by_name_.find(value);
+    return it == enum_values_by_name_.end() ? base::nullopt
+                                            : base::make_optional(it->second);
   }
 
   const std::string& file_name() const { return file_name_; }
@@ -141,7 +150,8 @@
   const Type type_;
   base::Optional<uint32_t> parent_id_;
   std::unordered_map<uint32_t, FieldDescriptor> fields_;
-  std::unordered_map<int32_t, std::string> enum_values_;
+  std::unordered_map<int32_t, std::string> enum_names_by_value_;
+  std::unordered_map<std::string, int32_t> enum_values_by_name_;
 };
 
 using ExtensionInfo = std::pair<std::string, protozero::ConstBytes>;
@@ -156,12 +166,16 @@
   base::Optional<uint32_t> FindDescriptorIdx(
       const std::string& full_name) const;
 
+  std::vector<uint8_t> SerializeAsDescriptorSet();
+
+  void AddProtoDescriptorForTesting(ProtoDescriptor descriptor) {
+    descriptors_.emplace_back(std::move(descriptor));
+  }
+
   const std::vector<ProtoDescriptor>& descriptors() const {
     return descriptors_;
   }
 
-  std::vector<uint8_t> SerializeAsDescriptorSet();
-
  private:
   base::Status AddNestedProtoDescriptors(const std::string& file_name,
                                          const std::string& package_name,
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 2440d39..848751e 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -50,14 +50,17 @@
   }
 }
 
-# Separate target because the embedder might not want this (e.g. on Windows).
+# Separate target because the embedder might not want this.
 source_set("platform_impl") {
   deps = [
     "../../gn:default_deps",
     "../../include/perfetto/tracing",
     "../base",
   ]
-  sources = [ "platform_posix.cc" ]
+  sources = [
+    "platform_posix.cc",
+    "platform_windows.cc",
+  ]
 }
 
 # Fake platform that allows buiding the client lib on all OSes. You can only use
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 7084dec..63839b0 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -57,6 +57,7 @@
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/base/version.h"
 #include "perfetto/ext/base/watchdog.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/consumer.h"
@@ -332,7 +333,8 @@
                                     bool in_process,
                                     ProducerSMBScrapingMode smb_scraping_mode,
                                     size_t shared_memory_page_size_hint_bytes,
-                                    std::unique_ptr<SharedMemory> shm) {
+                                    std::unique_ptr<SharedMemory> shm,
+                                    const std::string& sdk_version) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
   if (lockdown_mode_ && uid != base::GetCurrentUserId()) {
@@ -361,8 +363,8 @@
   }
 
   std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl(
-      id, uid, this, task_runner_, producer, producer_name, in_process,
-      smb_scraping_enabled));
+      id, uid, this, task_runner_, producer, producer_name, sdk_version,
+      in_process, smb_scraping_enabled));
   auto it_and_inserted = producers_.emplace(id, endpoint.get());
   PERFETTO_DCHECK(it_and_inserted.second);
   endpoint->shmem_size_hint_bytes_ = shared_memory_size_hint_bytes;
@@ -3039,7 +3041,7 @@
   tracing_session->did_emit_system_info = true;
   protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
   auto* info = packet->set_system_info();
-  base::ignore_result(info);  // For PERFETTO_OS_WIN.
+  info->set_tracing_service_version(base::GetVersionString());
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
     !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   struct utsname uname_info;
@@ -3479,6 +3481,7 @@
   TracingServiceState svc_state;
 
   const auto& sessions = service_->tracing_sessions_;
+  svc_state.set_tracing_service_version(base::GetVersionString());
   svc_state.set_num_sessions(static_cast<int>(sessions.size()));
 
   int num_started = 0;
@@ -3490,6 +3493,7 @@
     auto* producer = svc_state.add_producers();
     producer->set_id(static_cast<int>(kv.first));
     producer->set_name(kv.second->name_);
+    producer->set_sdk_version(kv.second->sdk_version_);
     producer->set_uid(static_cast<int32_t>(producer->uid()));
   }
 
@@ -3547,6 +3551,7 @@
     base::TaskRunner* task_runner,
     Producer* producer,
     const std::string& producer_name,
+    const std::string& sdk_version,
     bool in_process,
     bool smb_scraping_enabled)
     : id_(id),
@@ -3555,6 +3560,7 @@
       task_runner_(task_runner),
       producer_(producer),
       name_(producer_name),
+      sdk_version_(sdk_version),
       in_process_(in_process),
       smb_scraping_enabled_(smb_scraping_enabled),
       weak_ptr_factory_(this) {}
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 27b172c..401aaa1 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -87,6 +87,7 @@
                          base::TaskRunner*,
                          Producer*,
                          const std::string& producer_name,
+                         const std::string& sdk_version,
                          bool in_process,
                          bool smb_scraping_enabled);
     ~ProducerEndpointImpl() override;
@@ -154,6 +155,7 @@
     size_t shmem_page_size_hint_bytes_ = 0;
     bool is_shmem_provided_by_producer_ = false;
     const std::string name_;
+    std::string sdk_version_;
     bool in_process_;
     bool smb_scraping_enabled_;
 
@@ -291,7 +293,8 @@
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
       size_t shared_memory_page_size_hint_bytes = 0,
-      std::unique_ptr<SharedMemory> shm = nullptr) override;
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      const std::string& sdk_version = {}) override;
 
   std::unique_ptr<TracingService::ConsumerEndpoint> ConnectConsumer(
       Consumer*,
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
index 93c97a6..f86aeb5 100644
--- a/src/tracing/ipc/BUILD.gn
+++ b/src/tracing/ipc/BUILD.gn
@@ -32,6 +32,8 @@
     "memfd.h",
     "posix_shared_memory.cc",
     "posix_shared_memory.h",
+    "shared_memory_windows.cc",
+    "shared_memory_windows.h",
   ]
   deps = [
     "../../../gn:default_deps",
diff --git a/src/tracing/ipc/default_socket.cc b/src/tracing/ipc/default_socket.cc
index 3ad5cee..42b47e7 100644
--- a/src/tracing/ipc/default_socket.cc
+++ b/src/tracing/ipc/default_socket.cc
@@ -23,19 +23,22 @@
 #include "perfetto/ext/tracing/core/basic_types.h"
 
 #include <stdlib.h>
-#include <unistd.h>
 
-namespace perfetto {
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-// On non-Android platforms, check /run/perfetto/ before using /tmp/ as the
-// socket base directory.
-namespace {
-const char* kRunPerfettoBaseDir = "/run/perfetto/";
-
-bool UseRunPerfettoBaseDir() {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <unistd.h>
+#endif
+
+namespace perfetto {
+namespace {
+
+const char* kRunPerfettoBaseDir = "/run/perfetto/";
+
+// On Linux and CrOS, check /run/perfetto/ before using /tmp/ as the socket
+// base directory.
+bool UseRunPerfettoBaseDir() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
   // Note that the trailing / in |kRunPerfettoBaseDir| ensures we are checking
   // against a directory, not a file.
   int res = PERFETTO_EINTR(access(kRunPerfettoBaseDir, X_OK));
@@ -50,12 +53,12 @@
   }
   return false;
 #else
+  base::ignore_result(kRunPerfettoBaseDir);
   return false;
 #endif
 }
 
 }  // anonymous namespace
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
 
 static_assert(kInvalidUid == ipc::kInvalidUid, "kInvalidUid mismatching");
 
@@ -74,6 +77,7 @@
     name = producer_socket;
 #endif
   }
+  base::ignore_result(UseRunPerfettoBaseDir);  // Silence unused func warnings.
   return name;
 }
 
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 64b1023..97b622c 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -21,6 +21,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/version.h"
 #include "perfetto/ext/ipc/client.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/producer.h"
@@ -29,7 +30,12 @@
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "perfetto/tracing/core/trace_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
 #include "src/tracing/ipc/posix_shared_memory.h"
+#endif
 
 // TODO(fmayer): think to what happens when ProducerIPCClientImpl gets destroyed
 // w.r.t. the Producer pointer. Also think to lifetime of the Producer* during
@@ -158,8 +164,13 @@
 
   int shm_fd = -1;
   if (shared_memory_) {
-    shm_fd = static_cast<PosixSharedMemory*>(shared_memory_.get())->fd();
     req.set_producer_provided_shmem(true);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    auto key = static_cast<SharedMemoryWindows*>(shared_memory_.get())->key();
+    req.set_shm_key_windows(key);
+#else
+    shm_fd = static_cast<PosixSharedMemory*>(shared_memory_.get())->fd();
+#endif
   }
 
 #if PERFETTO_DCHECK_IS_ON()
@@ -169,6 +180,7 @@
   req.set_build_flags(
       protos::gen::InitializeConnectionRequest::BUILD_FLAGS_DCHECKS_OFF);
 #endif
+  req.set_sdk_version(base::GetVersionString());
   producer_port_.InitializeConnection(req, std::move(on_init), shm_fd);
 
   // Create the back channel to receive commands from the Service.
@@ -253,15 +265,25 @@
   }
 
   if (cmd.has_setup_tracing()) {
+    std::unique_ptr<SharedMemory> ipc_shared_memory;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    const std::string& shm_key = cmd.setup_tracing().shm_key_windows();
+    if (!shm_key.empty())
+      ipc_shared_memory = SharedMemoryWindows::Attach(shm_key);
+#else
     base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
     if (shmem_fd) {
+      // TODO(primiano): handle mmap failure in case of OOM.
+      ipc_shared_memory =
+          PosixSharedMemory::AttachToFd(std::move(shmem_fd),
+                                        /*require_seals_if_supported=*/false);
+    }
+#endif
+    if (ipc_shared_memory) {
       // This is the nominal case used in most configurations, where the service
       // provides the SMB.
       PERFETTO_CHECK(!is_shmem_provided_by_producer_ && !shared_memory_);
-      // TODO(primiano): handle mmap failure in case of OOM.
-      shared_memory_ =
-          PosixSharedMemory::AttachToFd(std::move(shmem_fd),
-                                        /*require_seals_if_supported=*/false);
+      shared_memory_ = std::move(ipc_shared_memory);
       shared_buffer_page_size_kb_ =
           cmd.setup_tracing().shared_buffer_page_size_kb();
       shared_memory_arbiter_ = SharedMemoryArbiter::CreateInstance(
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index cfa79b1..71abd8c 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -26,7 +26,12 @@
 #include "perfetto/ext/tracing/core/tracing_service.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
 #include "src/tracing/ipc/posix_shared_memory.h"
+#endif
 
 // The remote Producer(s) are not trusted. All the methods from the ProducerPort
 // IPC layer (e.g. RegisterDataSource()) must assume that the remote Producer is
@@ -93,7 +98,18 @@
   // If the producer provided an SMB, tell the service to attempt to adopt it.
   std::unique_ptr<SharedMemory> shmem;
   if (req.producer_provided_shmem()) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    if (!req.has_shm_key_windows() || req.shm_key_windows().empty()) {
+      PERFETTO_ELOG(
+          "shm_key_windows must be non-empty when "
+          "producer_provided_shmem = true");
+    } else {
+      shmem = SharedMemoryWindows::Attach(req.shm_key_windows());
+      // Attach() does error logging if something fails, no need to extra ELOGs.
+    }
+#else
     base::ScopedFile shmem_fd = ipc::Service::TakeReceivedFD();
+
     if (shmem_fd) {
       shmem = PosixSharedMemory::AttachToFd(
           std::move(shmem_fd), /*require_seals_if_supported=*/true);
@@ -107,6 +123,7 @@
           "InitializeConnectionRequest's producer_provided_shmem flag is set "
           "but the producer didn't provide an FD");
     }
+#endif
   }
 
   // ConnectProducer will call OnConnect() on the next task.
@@ -114,7 +131,8 @@
       producer.get(), client_info.uid(), req.producer_name(),
       req.shared_memory_size_hint_bytes(),
       /*in_process=*/false, smb_scraping_mode,
-      req.shared_memory_page_size_hint_bytes(), std::move(shmem));
+      req.shared_memory_page_size_hint_bytes(), std::move(shmem),
+      req.sdk_version());
 
   // Could happen if the service has too many producers connected.
   if (!producer->service_endpoint) {
@@ -455,10 +473,17 @@
     // Nominal case (% Chrome): service provides SMB.
     setup_tracing->set_shared_buffer_page_size_kb(
         static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    const std::string& shm_key =
+        static_cast<SharedMemoryWindows*>(service_endpoint->shared_memory())
+            ->key();
+    setup_tracing->set_shm_key_windows(shm_key);
+#else
     const int shm_fd =
         static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())
             ->fd();
     cmd.set_fd(shm_fd);
+#endif
   }
   async_producer_commands.Resolve(std::move(cmd));
 }
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.cc b/src/tracing/ipc/service/service_ipc_host_impl.cc
index d19fef6..5618b3c 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.cc
+++ b/src/tracing/ipc/service/service_ipc_host_impl.cc
@@ -20,10 +20,15 @@
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/ipc/host.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
-#include "src/tracing/ipc/posix_shared_memory.h"
 #include "src/tracing/ipc/service/consumer_ipc_service.h"
 #include "src/tracing/ipc/service/producer_ipc_service.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
+#include "src/tracing/ipc/posix_shared_memory.h"
+#endif
+
 namespace perfetto {
 
 // TODO(fmayer): implement per-uid connection limit (b/69093705).
@@ -66,8 +71,13 @@
 
 bool ServiceIPCHostImpl::DoStart() {
   // Create and initialize the platform-independent tracing business logic.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  std::unique_ptr<SharedMemory::Factory> shm_factory(
+      new SharedMemoryWindows::Factory());
+#else
   std::unique_ptr<SharedMemory::Factory> shm_factory(
       new PosixSharedMemory::Factory());
+#endif
   svc_ = TracingService::CreateInstance(std::move(shm_factory), task_runner_);
 
   if (!producer_ipc_port_ || !consumer_ipc_port_) {
diff --git a/src/tracing/ipc/shared_memory_windows.cc b/src/tracing/ipc/shared_memory_windows.cc
new file mode 100644
index 0000000..579d810
--- /dev/null
+++ b/src/tracing/ipc/shared_memory_windows.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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/ipc/shared_memory_windows.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <memory>
+#include <random>
+
+#include <Windows.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Create(size_t size) {
+  base::ScopedPlatformHandle shmem_handle;
+  std::random_device rnd_dev;
+  uint64_t rnd_key = (static_cast<uint64_t>(rnd_dev()) << 32) | rnd_dev();
+  std::string key = "perfetto_shm_" + base::Uint64ToHexStringNoPrefix(rnd_key);
+  shmem_handle.reset(CreateFileMappingA(
+      INVALID_HANDLE_VALUE,  // Use paging file.
+      nullptr,               // Default security.
+      PAGE_READWRITE,
+      static_cast<DWORD>(size >> 32),  // maximum object size (high-order DWORD)
+      static_cast<DWORD>(size),        // maximum object size (low-order DWORD)
+      key.c_str()));
+
+  if (!shmem_handle) {
+    PERFETTO_PLOG("CreateFileMapping() call failed");
+    return nullptr;
+  }
+  void* start =
+      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+                    /*offsetLow=*/0, size);
+  if (!start) {
+    PERFETTO_PLOG("MapViewOfFile() failed");
+    return nullptr;
+  }
+
+  return std::unique_ptr<SharedMemoryWindows>(new SharedMemoryWindows(
+      start, size, std::move(key), std::move(shmem_handle)));
+}
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Attach(
+    const std::string& key) {
+  base::ScopedPlatformHandle shmem_handle;
+  shmem_handle.reset(
+      OpenFileMappingA(FILE_MAP_ALL_ACCESS, /*inherit=*/false, key.c_str()));
+  if (!shmem_handle) {
+    PERFETTO_PLOG("Failed to OpenFileMapping()");
+    return nullptr;
+  }
+
+  void* start =
+      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+                    /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
+  if (!start) {
+    PERFETTO_PLOG("MapViewOfFile() failed");
+    return nullptr;
+  }
+
+  MEMORY_BASIC_INFORMATION info{};
+  if (!VirtualQuery(start, &info, sizeof(info))) {
+    PERFETTO_PLOG("VirtualQuery() failed");
+    return nullptr;
+  }
+  size_t size = info.RegionSize;
+  return std::unique_ptr<SharedMemoryWindows>(
+      new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+}
+
+SharedMemoryWindows::SharedMemoryWindows(void* start,
+                                         size_t size,
+                                         std::string key,
+                                         base::ScopedPlatformHandle handle)
+    : start_(start),
+      size_(size),
+      key_(std::move(key)),
+      handle_(std::move(handle)) {}
+
+SharedMemoryWindows::~SharedMemoryWindows() {
+  if (start_)
+    UnmapViewOfFile(start_);
+}
+
+SharedMemoryWindows::Factory::~Factory() = default;
+
+std::unique_ptr<SharedMemory> SharedMemoryWindows::Factory::CreateSharedMemory(
+    size_t size) {
+  return SharedMemoryWindows::Create(size);
+}
+
+}  // namespace perfetto
+
+#endif  // !OS_WIN
diff --git a/src/tracing/ipc/shared_memory_windows.h b/src/tracing/ipc/shared_memory_windows.h
new file mode 100644
index 0000000..803c8a8
--- /dev/null
+++ b/src/tracing/ipc/shared_memory_windows.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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_IPC_SHARED_MEMORY_WINDOWS_H_
+#define SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/tracing/core/shared_memory.h"
+
+namespace perfetto {
+
+// Implements the SharedMemory and its factory for the Windows IPC transport.
+// This used only for standalone builds and NOT in chromium, which instead uses
+// a custom Mojo wrapper (MojoSharedMemory in chromium's //services/tracing/).
+class SharedMemoryWindows : public SharedMemory {
+ public:
+  class Factory : public SharedMemory::Factory {
+   public:
+    ~Factory() override;
+    std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
+  };
+
+  // Create a brand new SHM region.
+  static std::unique_ptr<SharedMemoryWindows> Create(size_t size);
+  static std::unique_ptr<SharedMemoryWindows> Attach(const std::string& key);
+  ~SharedMemoryWindows() override;
+  const std::string& key() const { return key_; }
+
+  // SharedMemory implementation.
+  void* start() const override { return start_; }
+  size_t size() const override { return size_; }
+
+ private:
+  SharedMemoryWindows(void* start,
+                      size_t size,
+                      std::string,
+                      base::ScopedPlatformHandle);
+  SharedMemoryWindows(const SharedMemoryWindows&) = delete;
+  SharedMemoryWindows& operator=(const SharedMemoryWindows&) = delete;
+
+  void* const start_;
+  const size_t size_;
+  std::string key_;
+  base::ScopedPlatformHandle handle_;
+};
+
+}  // namespace perfetto
+
+#endif  // OS_WIN
+
+#endif  // SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
diff --git a/src/tracing/platform_windows.cc b/src/tracing/platform_windows.cc
new file mode 100644
index 0000000..651812a
--- /dev/null
+++ b/src/tracing/platform_windows.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <Windows.h>
+
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/tracing/internal/tracing_tls.h"
+#include "perfetto/tracing/platform.h"
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from chromium's base/threading/thread_local_storage_win.cc
+// which in turn is from http://www.codeproject.com/threads/tls.asp.
+
+#ifdef _WIN64
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:perfetto_thread_callback_base")
+#else
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_perfetto_thread_callback_base")
+#endif
+
+namespace perfetto {
+
+namespace {
+
+class PlatformWindows : public Platform {
+ public:
+  static PlatformWindows* instance;
+  PlatformWindows();
+  ~PlatformWindows() override;
+
+  ThreadLocalObject* GetOrCreateThreadLocalObject() override;
+  std::unique_ptr<base::TaskRunner> CreateTaskRunner(
+      const CreateTaskRunnerArgs&) override;
+  std::string GetCurrentProcessName() override;
+  void OnThreadExit();
+
+ private:
+  DWORD tls_key_{};
+};
+
+using ThreadLocalObject = Platform::ThreadLocalObject;
+
+// static
+PlatformWindows* PlatformWindows::instance = nullptr;
+
+PlatformWindows::PlatformWindows() {
+  instance = this;
+  tls_key_ = ::TlsAlloc();
+  PERFETTO_CHECK(tls_key_ != TLS_OUT_OF_INDEXES);
+}
+
+PlatformWindows::~PlatformWindows() {
+  ::TlsFree(tls_key_);
+  instance = nullptr;
+}
+
+void PlatformWindows::OnThreadExit() {
+  auto tls = static_cast<ThreadLocalObject*>(::TlsGetValue(tls_key_));
+  if (tls) {
+    // At this point we rely on the TLS object to be still set to the TracingTLS
+    // we are deleting. See comments in TracingTLS::~TracingTLS().
+    delete tls;
+  }
+}
+
+ThreadLocalObject* PlatformWindows::GetOrCreateThreadLocalObject() {
+  void* tls_ptr = ::TlsGetValue(tls_key_);
+
+  auto* tls = static_cast<ThreadLocalObject*>(tls_ptr);
+  if (!tls) {
+    tls = ThreadLocalObject::CreateInstance().release();
+    ::TlsSetValue(tls_key_, tls);
+  }
+  return tls;
+}
+
+std::unique_ptr<base::TaskRunner> PlatformWindows::CreateTaskRunner(
+    const CreateTaskRunnerArgs&) {
+  return std::unique_ptr<base::TaskRunner>(
+      new base::ThreadTaskRunner(base::ThreadTaskRunner::CreateAndStart()));
+}
+
+std::string PlatformWindows::GetCurrentProcessName() {
+  char buf[MAX_PATH];
+  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+  std::string name(buf, static_cast<size_t>(len));
+  size_t sep = name.find_last_of('\\');
+  if (sep != std::string::npos)
+    name = name.substr(sep + 1);
+  return name;
+}
+
+}  // namespace
+
+// static
+Platform* Platform::GetDefaultPlatform() {
+  static PlatformWindows* thread_safe_init_instance = new PlatformWindows();
+  return thread_safe_init_instance;
+}
+
+}  // namespace perfetto
+
+// -----------------------
+// Thread-local destructor
+// -----------------------
+
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// See VC\crt\src\tlssup.c for reference.
+
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard perfetto_thread_callback_base. (We force a
+// reference to this variable with a linker /INCLUDE:symbol pragma to ensure
+// that.) If this variable is discarded, the OnThreadExit function will never be
+// called.
+
+void NTAPI PerfettoOnThreadExit(PVOID, DWORD, PVOID);
+void NTAPI PerfettoOnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
+  if (reason == DLL_THREAD_DETACH || reason == DLL_PROCESS_DETACH) {
+    if (perfetto::PlatformWindows::instance)
+      perfetto::PlatformWindows::instance->OnThreadExit();
+  }
+}
+
+#ifdef _WIN64
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLP")
+
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base;
+const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
+
+// Reset the default section.
+#pragma const_seg()
+
+#else  // _WIN64
+
+#pragma data_seg(".CRT$XLP")
+PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = OnThreadExit;
+// Reset the default section.
+#pragma data_seg()
+
+#endif  // _WIN64
+
+}  // extern "C"
+
+#endif  // OS_WIN
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index f7cfc1a..a0205f1 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -134,5 +134,55 @@
     AssertNoProfileContents(packets);
 }
 
+TEST(HeapprofdJavaCtsTest, DebuggableAppRuntimeByPid) {
+  std::string app_name = "android.perfetto.cts.app.debuggable";
+
+  base::TestTaskRunner task_runner;
+
+  // (re)start the target app's main activity
+  if (IsAppRunning(app_name)) {
+    StopApp(app_name, "old.app.stopped", &task_runner);
+    task_runner.RunUntilCheckpoint("old.app.stopped", 1000 /*ms*/);
+  }
+  StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
+                   /*delay_ms=*/100);
+  task_runner.RunUntilCheckpoint("target.app.running", 1000 /*ms*/);
+  // If we try to dump too early in app initialization, we sometimes deadlock.
+  sleep(1);
+
+  int target_pid = PidForProcessName(app_name);
+  ASSERT_NE(target_pid, -1);
+
+  // set up tracing
+  TestHelper helper(&task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(40 * 1024);
+  trace_config.set_duration_ms(6000);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.java_hprof");
+  ds_config->set_target_buffer(0);
+
+  protos::gen::JavaHprofConfig java_hprof_config;
+  java_hprof_config.add_pid(static_cast<uint64_t>(target_pid));
+  ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
+
+  // start tracing
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled();
+  helper.ReadData();
+  helper.WaitForReadData();
+  PERFETTO_CHECK(IsAppRunning(app_name));
+  StopApp(app_name, "new.app.stopped", &task_runner);
+  task_runner.RunUntilCheckpoint("new.app.stopped", 1000 /*ms*/);
+
+  const auto& packets = helper.trace();
+  AssertGraphPresent(packets);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/test/test_helper.h b/test/test_helper.h
index 4ac4c46..d9a99b9 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -34,8 +34,7 @@
 #include "test/fake_producer.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// TODO(primiano): uncomment in next CL.
-// #include "src/tracing/ipc/shared_memory_windows.h"
+#include "src/tracing/ipc/shared_memory_windows.h"
 #else
 #include "src/traced/probes/probes_producer.h"
 #include "src/tracing/ipc/posix_shared_memory.h"
@@ -168,12 +167,12 @@
 
   void CreateProducerProvidedSmb() {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // TODO(primiano): in next CLs introduce SharedMemoryWindows.
+    SharedMemoryWindows::Factory factory;
 #else
     PosixSharedMemory::Factory factory;
+#endif
     shm_ = factory.CreateSharedMemory(1024 * 1024);
     shm_arbiter_ = SharedMemoryArbiter::CreateUnboundInstance(shm_.get(), 4096);
-#endif
   }
 
   void ProduceStartupEventBatch(const protos::gen::TestConfig& config,
diff --git a/test/trace_processor/parsing/counters_json_counters.out b/test/trace_processor/parsing/counters_json_counters.out
new file mode 100644
index 0000000..dfe4747
--- /dev/null
+++ b/test/trace_processor/parsing/counters_json_counters.out
@@ -0,0 +1,4 @@
+"name","ts","value"
+"ctr cats",0,0.000000
+"ctr cats",10000,10.000000
+"ctr cats",20000,0.000000
diff --git a/test/trace_processor/parsing/index b/test/trace_processor/parsing/index
index 4b2ca9a..65ef807 100644
--- a/test/trace_processor/parsing/index
+++ b/test/trace_processor/parsing/index
@@ -142,3 +142,7 @@
 
 # Floating point numbers
 ../../data/decimal_timestamp.json slices.sql decimal_timestamp_slices.out
+
+# JSON instants and counters
+../../data/counters.json json_counters.sql counters_json_counters.out
+../../data/instants.json json_instants.sql instants_json_instants.out
diff --git a/test/trace_processor/parsing/instants_json_instants.out b/test/trace_processor/parsing/instants_json_instants.out
new file mode 100644
index 0000000..2fb9fb4
--- /dev/null
+++ b/test/trace_processor/parsing/instants_json_instants.out
@@ -0,0 +1,5 @@
+"ts","slice_name","tid","pid"
+1234523300,"Thread",2347,"[NULL]"
+1235523300,"Global","[NULL]","[NULL]"
+1236523300,"Process","[NULL]",2320
+1237523300,"None",6790,"[NULL]"
diff --git a/test/trace_processor/parsing/json_counters.sql b/test/trace_processor/parsing/json_counters.sql
new file mode 100644
index 0000000..604c2c1
--- /dev/null
+++ b/test/trace_processor/parsing/json_counters.sql
@@ -0,0 +1,6 @@
+select
+  process_counter_track.name,
+  counter.ts,
+  counter.value
+from counter
+join process_counter_track on (counter.track_id = process_counter_track.id);
\ No newline at end of file
diff --git a/test/trace_processor/parsing/json_instants.sql b/test/trace_processor/parsing/json_instants.sql
new file mode 100644
index 0000000..d76ef89
--- /dev/null
+++ b/test/trace_processor/parsing/json_instants.sql
@@ -0,0 +1,12 @@
+select
+  slice.ts,
+  slice.name as slice_name,
+  thread.tid,
+  process.pid
+from slice
+join track on (slice.track_id = track.id)
+left join thread_track on (slice.track_id = thread_track.id)
+left join thread on (thread_track.utid = thread.utid)
+left join process_track on (slice.track_id = process_track.id)
+left join process on (process_track.upid = process.upid)
+where dur = 0;
\ No newline at end of file
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 5ae4b85..cc8094b 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -163,9 +163,9 @@
   for line in (line.strip() for line in lines if not line.startswith('#')):
     assert os.path.exists(line), 'file %s should exist' % line
     if line.startswith('test/data/'):
-        # Skip test data files that require GCS. They are only for benchmarks.
-        # We don't run benchmarks in the android tree.
-        continue
+      # Skip test data files that require GCS. They are only for benchmarks.
+      # We don't run benchmarks in the android tree.
+      continue
     if line.endswith('/.'):
       yield line[:-1] + '**/*'
     else:
@@ -182,16 +182,16 @@
         ('cflags', {'-Wglobal-constructors', '-Werror=global-constructors'}),
         ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
         ('stubs', {
-          'versions': ['S'],
-          'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
+            'versions': ['S'],
+            'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
         }),
         ('export_include_dirs', {'src/profiling/memory/include'}),
     ],
     'heapprofd_api_noop': [
         ('version_script', 'src/profiling/memory/heapprofd_client_api.map.txt'),
         ('stubs', {
-          'versions': ['S'],
-          'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
+            'versions': ['S'],
+            'symbol_file': 'src/profiling/memory/heapprofd_client_api.map.txt',
         }),
         ('export_include_dirs', {'src/profiling/memory/include'}),
     ],
@@ -210,42 +210,42 @@
         ('include_dirs', {'bionic/libc/kernel'}),
     ],
     'perfetto_integrationtests': [
-      ('test_suites', {'general-tests'}),
-      ('test_config', 'PerfettoIntegrationTests.xml'),
+        ('test_suites', {'general-tests'}),
+        ('test_config', 'PerfettoIntegrationTests.xml'),
     ],
-    'traced_probes': [
-        ('required', {'libperfetto_android_internal',
-                      'trigger_perfetto',
-                      'traced_perf',
-                      'mm_events'}),
-    ],
+    'traced_probes': [('required', {
+        'libperfetto_android_internal', 'trigger_perfetto', 'traced_perf',
+        'mm_events'
+    }),],
     'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
     'trace_processor_shell': [
-      ('strip', {'all': True}),
-      ('host', {
-        'stl': 'libc++_static',
-        'dist': {'targets': ['sdk_repo']},
-      }),
+        ('strip', {
+            'all': True
+        }),
+        ('host', {
+            'stl': 'libc++_static',
+            'dist': {
+                'targets': ['sdk_repo']
+            },
+        }),
     ],
     'libperfetto_client_experimental': [
-      ('apex_available', {
-        '//apex_available:platform',
-        'com.android.art',
-        'com.android.art.debug'}),
-      ('min_sdk_version', 'S'),
-      ('shared_libs', {'liblog'}),
-      ('export_include_dirs', {'include', buildflags_dir}),
+        ('apex_available', {
+            '//apex_available:platform', 'com.android.art',
+            'com.android.art.debug'
+        }),
+        ('min_sdk_version', 'S'),
+        ('shared_libs', {'liblog'}),
+        ('export_include_dirs', {'include', buildflags_dir}),
     ],
     'perfetto_trace_protos': [
-      ('apex_available', {
-        '//apex_available:platform',
-        'com.android.art',
-        'com.android.art.debug'}),
-      ('min_sdk_version', 'S'),
+        ('apex_available', {
+            '//apex_available:platform', 'com.android.art',
+            'com.android.art.debug'
+        }),
+        ('min_sdk_version', 'S'),
     ],
-    'libperfetto': [
-      ('export_include_dirs', {'include', buildflags_dir}),
-    ],
+    'libperfetto': [('export_include_dirs', {'include', buildflags_dir}),],
 }
 
 
@@ -325,6 +325,7 @@
 def enable_uapi_headers(module):
   module.include_dirs.add('bionic/libc/kernel')
 
+
 def enable_bionic_libc_platform_headers_on_android(module):
   module.header_libs.add('bionic_libc_platform_headers')
 
@@ -332,20 +333,32 @@
 # Android equivalents for third-party libraries that the upstream project
 # depends on.
 builtin_deps = {
-    '//gn:default_deps': lambda x: None,
-    '//gn:gtest_main': lambda x: None,
-    '//gn:protoc': lambda x: None,
-    '//gn:gtest_and_gmock': enable_gtest_and_gmock,
-    '//gn:libunwind': enable_libunwind,
-    '//gn:protobuf_full': enable_protobuf_full,
-    '//gn:protobuf_lite': enable_protobuf_lite,
-    '//gn:protoc_lib': enable_protoc_lib,
-    '//gn:libunwindstack': enable_libunwindstack,
-    '//gn:sqlite': enable_sqlite,
-    '//gn:zlib': enable_zlib,
-    '//gn:bionic_kernel_uapi_headers' : enable_uapi_headers,
+    '//gn:default_deps':
+        lambda x: None,
+    '//gn:gtest_main':
+        lambda x: None,
+    '//gn:protoc':
+        lambda x: None,
+    '//gn:gtest_and_gmock':
+        enable_gtest_and_gmock,
+    '//gn:libunwind':
+        enable_libunwind,
+    '//gn:protobuf_full':
+        enable_protobuf_full,
+    '//gn:protobuf_lite':
+        enable_protobuf_lite,
+    '//gn:protoc_lib':
+        enable_protoc_lib,
+    '//gn:libunwindstack':
+        enable_libunwindstack,
+    '//gn:sqlite':
+        enable_sqlite,
+    '//gn:zlib':
+        enable_zlib,
+    '//gn:bionic_kernel_uapi_headers':
+        enable_uapi_headers,
     '//src/profiling/memory:bionic_libc_platform_headers_on_android':
-      enable_bionic_libc_platform_headers_on_android,
+        enable_bionic_libc_platform_headers_on_android,
 }
 
 # ----------------------------------------------------------------------------
@@ -546,7 +559,6 @@
     output.append('}')
     output.append('')
 
-
   def add_android_static_lib(self, lib):
     if self.type == 'cc_binary_host':
       raise Exception('Adding Android static lib for host tool is unsupported')
@@ -555,7 +567,6 @@
     else:
       self.static_libs.add(lib)
 
-
   def add_android_shared_lib(self, lib):
     if self.type == 'cc_binary_host':
       raise Exception('Adding Android shared lib for host tool is unsupported')
@@ -564,7 +575,6 @@
     else:
       self.shared_libs.add(lib)
 
-
   def _output_field(self, output, name, sort=True):
     value = getattr(self, name)
     return write_blueprint_key_value(output, name, value, sort)
@@ -767,10 +777,8 @@
       'tools/gen_cc_proto_descriptor.py',
   ]
   module.cmd = ' '.join([
-      '$(location tools/gen_cc_proto_descriptor.py)',
-      '--gen_dir=$(genDir)',
-      '--cpp_out=$(out)',
-      '$(in)'
+      '$(location tools/gen_cc_proto_descriptor.py)', '--gen_dir=$(genDir)',
+      '--cpp_out=$(out)', '$(in)'
   ])
   module.genrule_headers.add(module.name)
   module.srcs.update(
@@ -784,14 +792,12 @@
   module = Module('genrule', bp_module_name, gn_utils.GEN_VERSION_TARGET)
   script_path = gn_utils.label_to_path(target.script)
   module.genrule_headers.add(bp_module_name)
-  module.tool_files = [ script_path ]
+  module.tool_files = [script_path]
   module.out.update(target.outputs)
   module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
   module.cmd = ' '.join([
-        'python3 $(location %s)' % script_path,
-        '--no_git',
-        '--changelog=$(location CHANGELOG)',
-        '--cpp_out=$(out)'
+      'python3 $(location %s)' % script_path, '--no_git',
+      '--changelog=$(location CHANGELOG)', '--cpp_out=$(out)'
   ])
   blueprint.add_module(module)
   return module
@@ -821,6 +827,7 @@
     return blueprint.modules[bp_module_name]
   target = gn.get_target(gn_target_name)
 
+  name_without_toolchain = gn_utils.label_without_toolchain(target.name)
   if target.type == 'executable':
     if target.toolchain == gn_utils.HOST_TOOLCHAIN:
       module_type = 'cc_binary_host'
@@ -846,10 +853,10 @@
   elif target.type == 'action':
     if 'gen_merged_sql_metrics' in target.name:
       module = create_merged_sql_metrics_module(blueprint, target)
-    elif re.match('.*gen_cc_.*_descriptor$', target.name):
+    elif re.match('.*gen_cc_.*_descriptor$', name_without_toolchain):
       module = create_cc_proto_descriptor_module(blueprint, target)
-    elif target.type == 'action' and gn_utils.label_without_toolchain(
-        target.name) == gn_utils.GEN_VERSION_TARGET:
+    elif target.type == 'action' and \
+        name_without_toolchain == gn_utils.GEN_VERSION_TARGET:
       module = create_gen_version_module(blueprint, target, bp_module_name)
     else:
       raise Error('Unhandled action: {}'.format(target.name))
@@ -857,8 +864,7 @@
     raise Error('Unknown target %s (%s)' % (target.name, target.type))
 
   blueprint.add_module(module)
-  module.host_supported = (gn_utils.label_without_toolchain(target.name) in
-                           target_host_supported)
+  module.host_supported = (name_without_toolchain in target_host_supported)
   module.init_rc = target_initrc.get(target.name, [])
   module.srcs.update(
       gn_utils.label_to_path(src)
@@ -1033,7 +1039,7 @@
   # perfetto_component.gni is fixed.
   # Check for ODR violations
   # for target_name in default_targets:
-    # checker = gn_utils.ODRChecker(gn, target_name)
+  # checker = gn_utils.ODRChecker(gn, target_name)
 
   output = [
       """// Copyright (C) 2017 The Android Open Source Project
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 878f1f6..6575bea 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -234,8 +234,8 @@
     # Example traces for regression tests.
     Dependency(
         'test/data.zip',
-        'https://storage.googleapis.com/perfetto/test-data-20210513-224349.zip',
-        '3dcc146f4ce38d17fd1f8c4c65af07e7cf7c5c4cb8aa4c7bf73ec3a095d997d1',
+        'https://storage.googleapis.com/perfetto/test-data-20210518-223638.zip',
+        'a9119ae4828fae92c9ec8449e3fd91d753e60b60fa59611fb8e6c0dd6ed69b13',
         'all', 'all',
     ),
 
diff --git a/tools/java_heap_dump b/tools/java_heap_dump
index 8e0f31f..5f301b6 100755
--- a/tools/java_heap_dump
+++ b/tools/java_heap_dump
@@ -24,6 +24,7 @@
 import sys
 import tempfile
 import time
+import uuid
 
 NULL = open(os.devnull)
 
@@ -61,9 +62,11 @@
       }}
 """
 
+UUID = str(uuid.uuid4())[-6:]
+PROFILE_PATH = '/data/misc/perfetto-traces/java-profile-' +  UUID
+
 PERFETTO_CMD = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
-                'perfetto --txt -c - -o '
-                '/data/misc/perfetto-traces/java-profile-{user} -d')
+                'perfetto --txt -c - -o ' + PROFILE_PATH + ' -d')
 
 SDK = {
     'S': 31,
@@ -212,8 +215,10 @@
     time.sleep(1)
 
   subprocess.check_call(
-    ['adb', 'pull', '/data/misc/perfetto-traces/java-profile-{}'.format(user),
-     output_file], stdout=NULL)
+    ['adb', 'pull', PROFILE_PATH, output_file], stdout=NULL)
+
+  subprocess.check_call(
+        ['adb', 'shell', 'rm', PROFILE_PATH], stdout=NULL)
 
   print("Wrote profile to {}".format(output_file))
   print("This can be viewed using https://ui.perfetto.dev.")
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 7c409a3..503e688 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("../../gn/perfetto.gni")
+import("../../gn/perfetto_cc_proto_descriptor.gni")
 import("../../gn/perfetto_host_executable.gni")
 import("../../gn/wasm.gni")
 
@@ -131,6 +132,7 @@
   testonly = true
   deps = [
     ":common",
+    ":gen_cc_trace_descriptor",
     ":utils",
     "../../gn:default_deps",
     "../../gn:protobuf_full",
@@ -155,3 +157,8 @@
     ]
   }
 }
+
+perfetto_cc_proto_descriptor("gen_cc_trace_descriptor") {
+  descriptor_name = "trace.descriptor"
+  descriptor_target = "../../protos/perfetto/trace:descriptor"
+}
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index c71a827..5b3fc4f 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -59,10 +59,9 @@
 std::string GetRandomString(size_t n) {
   std::random_device r;
   auto rng = std::default_random_engine(r());
-  std::uniform_int_distribution<char> dist('a', 'z');
   std::string result(n, ' ');
   for (size_t i = 0; i < n; ++i) {
-    result[i] = dist(rng);
+    result[i] = 'a' + (rng() % ('z' - 'a'));
   }
   return result;
 }
diff --git a/tools/trace_to_text/trace_to_text.cc b/tools/trace_to_text/trace_to_text.cc
index 2fef5a5..070bb14 100644
--- a/tools/trace_to_text/trace_to_text.cc
+++ b/tools/trace_to_text/trace_to_text.cc
@@ -16,7 +16,6 @@
 
 #include "tools/trace_to_text/trace_to_text.h"
 
-#include <google/protobuf/compiler/importer.h>
 #include <google/protobuf/dynamic_message.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/text_format.h>
@@ -25,6 +24,7 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "tools/trace_to_text/proto_full_utils.h"
+#include "tools/trace_to_text/trace.descriptor.h"
 #include "tools/trace_to_text/utils.h"
 
 #include "protos/perfetto/trace/trace.pbzero.h"
@@ -36,17 +36,17 @@
 
 namespace perfetto {
 namespace trace_to_text {
-
 namespace {
+
 using google::protobuf::Descriptor;
+using google::protobuf::DescriptorPool;
 using google::protobuf::DynamicMessageFactory;
 using google::protobuf::FieldDescriptor;
 using google::protobuf::FileDescriptor;
+using google::protobuf::FileDescriptorSet;
 using google::protobuf::Message;
 using google::protobuf::Reflection;
 using google::protobuf::TextFormat;
-using google::protobuf::compiler::DiskSourceTree;
-using google::protobuf::compiler::Importer;
 using google::protobuf::io::OstreamOutputStream;
 using google::protobuf::io::ZeroCopyOutputStream;
 
@@ -147,42 +147,34 @@
 }  // namespace
 
 int TraceToText(std::istream* input, std::ostream* output) {
-  const std::string proto_path = "protos/perfetto/trace/trace_packet.proto";
-
-  if (!base::OpenFile(proto_path, O_RDONLY)) {
-    PERFETTO_ELOG("Cannot open %s.", proto_path.c_str());
-    PERFETTO_ELOG(
-        "Text mode only works from the perfetto directory. Googlers, see "
-        "b/131425913");
-    return 1;
+  DescriptorPool pool;
+  FileDescriptorSet desc_set;
+  desc_set.ParseFromArray(kTraceDescriptor.data(), kTraceDescriptor.size());
+  for (const auto& desc : desc_set.file()) {
+    pool.BuildFile(desc);
   }
 
-  DiskSourceTree dst;
-  dst.MapPath("", "");
-  MultiFileErrorCollectorImpl mfe;
-  Importer importer(&dst, &mfe);
-  const FileDescriptor* parsed_file =
-      importer.Import("protos/perfetto/trace/trace_packet.proto");
+  DynamicMessageFactory factory(&pool);
+  const Descriptor* trace_descriptor =
+      pool.FindMessageTypeByName("perfetto.protos.TracePacket");
+  const Message* prototype = factory.GetPrototype(trace_descriptor);
+  std::unique_ptr<Message> msg(prototype->New());
 
-  DynamicMessageFactory dmf;
-  const Descriptor* trace_descriptor = parsed_file->message_type(0);
-  const Message* root = dmf.GetPrototype(trace_descriptor);
   OstreamOutputStream zero_copy_output(output);
   OstreamOutputStream* zero_copy_output_ptr = &zero_copy_output;
-  Message* msg = root->New();
 
   constexpr uint32_t kCompressedPacketFieldDescriptor = 50;
   const Reflection* reflect = msg->GetReflection();
   const FieldDescriptor* compressed_desc =
       trace_descriptor->FindFieldByNumber(kCompressedPacketFieldDescriptor);
-  Message* compressed_msg_scratch = root->New();
+  std::unique_ptr<Message> compressed_msg_scratch(prototype->New());
   std::string compressed_packet_scratch;
 
   TextFormat::Printer printer;
   printer.SetInitialIndentLevel(1);
   ForEachPacketBlobInTrace(
-      input, [msg, reflect, compressed_desc, zero_copy_output_ptr,
-              &compressed_packet_scratch, compressed_msg_scratch,
+      input, [&msg, reflect, compressed_desc, zero_copy_output_ptr,
+              &compressed_packet_scratch, &compressed_msg_scratch,
               &printer](std::unique_ptr<char[]> buf, size_t size) {
         if (!msg->ParseFromArray(buf.get(), static_cast<int>(size))) {
           PERFETTO_ELOG("Skipping invalid packet");
@@ -191,7 +183,8 @@
         if (reflect->HasField(*msg, compressed_desc)) {
           const auto& compressed_packets = reflect->GetStringReference(
               *msg, compressed_desc, &compressed_packet_scratch);
-          PrintCompressedPackets(compressed_packets, compressed_msg_scratch,
+          PrintCompressedPackets(compressed_packets,
+                                 compressed_msg_scratch.get(),
                                  zero_copy_output_ptr);
         } else {
           WriteToZeroCopyOutput(zero_copy_output_ptr, kPacketPrefix,
diff --git a/ui/release/channels.json b/ui/release/channels.json
index ffb232f..b6827d6 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "465b9a4473353091c7048b53ad90607295f86a4a"
+      "rev": "85ad7248ca898e29e71a6e76ea85f070c4c3e3a9"
     },
     {
       "name": "canary",
-      "rev": "a284e65f883e7c543c2619115be1ee0fbe5644a3"
+      "rev": "dac610dd857535ee840a1cdcc2a5103e59a99388"
     },
     {
       "name": "autopush",
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 26f343b..9db1325 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -569,7 +569,9 @@
           process.pid as pid
         from process_track
         left join process using(upid)
-        where process_track.name not like "% Timeline"
+        where
+            process_track.name is null or
+            process_track.name not like "% Timeline"
         group by
           process_track.upid,
           process_track.name
diff --git a/ui/src/tracks/actual_frames/controller.ts b/ui/src/tracks/actual_frames/controller.ts
index b436fbe..58140f7 100644
--- a/ui/src/tracks/actual_frames/controller.ts
+++ b/ui/src/tracks/actual_frames/controller.ts
@@ -12,8 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {assertTrue} from '../../base/logging';
-import {slowlyCountRows} from '../../common/query_iterator';
+import {assertExists, assertTrue} from '../../base/logging';
+import {
+  iter,
+  NUM,
+  singleRow,
+  slowlyCountRows,
+  STR
+} from '../../common/query_iterator';
 import {fromNs, toNs} from '../../common/time';
 import {
   TrackController,
@@ -46,26 +52,27 @@
 
     if (this.maxDurNs === 0) {
       const maxDurResult = await this.query(`
-        select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
+        select
+          max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
+            as maxDur
         from experimental_slice_layout
         where filter_track_ids = '${this.config.trackIds.join(',')}'
       `);
-      if (slowlyCountRows(maxDurResult) === 1) {
-        this.maxDurNs = maxDurResult.columns[0].longValues![0];
-      }
+      const row = singleRow({maxDur: NUM}, maxDurResult);
+      this.maxDurNs = assertExists(row).maxDur;
     }
 
     const rawResult = await this.query(`
       SELECT
         (s.ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
-        s.ts,
+        s.ts as ts,
         max(iif(s.dur = -1, (SELECT end_ts FROM trace_bounds) - s.ts, s.dur))
             as dur,
-        s.layout_depth,
-        s.name,
-        s.id,
-        s.dur = 0 as is_instant,
-        s.dur = -1 as is_incomplete,
+        s.layout_depth as layoutDepth,
+        s.name as name,
+        s.id as id,
+        s.dur = 0 as isInstant,
+        s.dur = -1 as isIncomplete,
         CASE afs.jank_tag
           WHEN 'Self Jank' THEN '${RED_COLOR}'
           WHEN 'Other Jank' THEN '${YELLOW_COLOR}'
@@ -112,11 +119,23 @@
       return idx;
     }
 
-    const cols = rawResult.columns;
-    for (let row = 0; row < numRows; row++) {
-      const startNsQ = +cols[0].longValues![row];
-      const startNs = +cols[1].longValues![row];
-      const durNs = +cols[2].longValues![row];
+    const it = iter(
+        {
+          'tsq': NUM,
+          'ts': NUM,
+          'dur': NUM,
+          'layoutDepth': NUM,
+          'id': NUM,
+          'name': STR,
+          'isInstant': NUM,
+          'isIncomplete': NUM,
+          'color': STR,
+        },
+        rawResult);
+    for (let i = 0; it.valid(); i++, it.next()) {
+      const startNsQ = it.row.tsq;
+      const startNs = it.row.ts;
+      const durNs = it.row.dur;
       const endNs = startNs + durNs;
 
       let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
@@ -124,14 +143,14 @@
 
       assertTrue(startNsQ !== endNsQ);
 
-      slices.starts[row] = fromNs(startNsQ);
-      slices.ends[row] = fromNs(endNsQ);
-      slices.depths[row] = +cols[3].longValues![row];
-      slices.titles[row] = internString(cols[4].stringValues![row]);
-      slices.colors![row] = internString(cols[8].stringValues![row]);
-      slices.sliceIds[row] = +cols[5].longValues![row];
-      slices.isInstant[row] = +cols[6].longValues![row];
-      slices.isIncomplete[row] = +cols[7].longValues![row];
+      slices.starts[i] = fromNs(startNsQ);
+      slices.ends[i] = fromNs(endNsQ);
+      slices.depths[i] = it.row.layoutDepth;
+      slices.titles[i] = internString(it.row.name);
+      slices.colors![i] = internString(it.row.color);
+      slices.sliceIds[i] = it.row.id;
+      slices.isInstant[i] = it.row.isInstant;
+      slices.isIncomplete[i] = it.row.isIncomplete;
     }
     return slices;
   }
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index 1389cb4..d6ac723 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -88,7 +88,7 @@
         `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
     const queryTable =
         isCached ? this.tableName('sched_cached') : this.tableName('sched');
-    const constainColumn = isCached ? 'cached_tsq' : 'ts';
+    const constraintColumn = isCached ? 'cached_tsq' : 'ts';
 
     const rawResult = await this.query(`
       select
@@ -99,8 +99,8 @@
         id
       from ${queryTable}
       where
-        ${constainColumn} >= ${startNs - this.maxDurNs} and
-        ${constainColumn} <= ${endNs}
+        ${constraintColumn} >= ${startNs - this.maxDurNs} and
+        ${constraintColumn} <= ${endNs}
       group by tsq
       order by tsq
     `);
diff --git a/ui/src/tracks/expected_frames/controller.ts b/ui/src/tracks/expected_frames/controller.ts
index 532eaa0..9707059 100644
--- a/ui/src/tracks/expected_frames/controller.ts
+++ b/ui/src/tracks/expected_frames/controller.ts
@@ -12,7 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {slowlyCountRows} from '../../common/query_iterator';
+import {assertExists} from '../../base/logging';
+import {
+  iter,
+  NUM,
+  singleRow,
+  slowlyCountRows,
+  STR
+} from '../../common/query_iterator';
 import {fromNs, toNs} from '../../common/time';
 import {
   TrackController,
@@ -39,12 +46,12 @@
     if (this.maxDurNs === 0) {
       const maxDurResult = await this.query(`
         select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
+          as maxDur
         from experimental_slice_layout
         where filter_track_ids = '${this.config.trackIds.join(',')}'
       `);
-      if (slowlyCountRows(maxDurResult) === 1) {
-        this.maxDurNs = maxDurResult.columns[0].longValues![0];
-      }
+      const row = singleRow({maxDur: NUM}, maxDurResult);
+      this.maxDurNs = assertExists(row).maxDur;
     }
 
     const rawResult = await this.query(`
@@ -52,11 +59,11 @@
         (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
         ts,
         max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
-        layout_depth,
+        layout_depth as layoutDepth,
         name,
         id,
-        dur = 0 as is_instant,
-        dur = -1 as is_incomplete
+        dur = 0 as isInstant,
+        dur = -1 as isIncomplete
       from experimental_slice_layout
       where
         filter_track_ids = '${this.config.trackIds.join(',')}' and
@@ -94,11 +101,22 @@
     }
     const greenIndex = internString('#4CAF50');
 
-    const cols = rawResult.columns;
-    for (let row = 0; row < numRows; row++) {
-      const startNsQ = +cols[0].longValues![row];
-      const startNs = +cols[1].longValues![row];
-      const durNs = +cols[2].longValues![row];
+    const it = iter(
+        {
+          tsq: NUM,
+          ts: NUM,
+          dur: NUM,
+          layoutDepth: NUM,
+          id: NUM,
+          name: STR,
+          isInstant: NUM,
+          isIncomplete: NUM,
+        },
+        rawResult);
+    for (let i = 0; it.valid(); it.next(), ++i) {
+      const startNsQ = it.row.tsq;
+      const startNs = it.row.ts;
+      const durNs = it.row.dur;
       const endNs = startNs + durNs;
 
       let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
@@ -108,14 +126,14 @@
         throw new Error('Should never happen');
       }
 
-      slices.starts[row] = fromNs(startNsQ);
-      slices.ends[row] = fromNs(endNsQ);
-      slices.depths[row] = +cols[3].longValues![row];
-      slices.titles[row] = internString(cols[4].stringValues![row]);
-      slices.sliceIds[row] = +cols[5].longValues![row];
-      slices.isInstant[row] = +cols[6].longValues![row];
-      slices.isIncomplete[row] = +cols[7].longValues![row];
-      slices.colors![row] = greenIndex;
+      slices.starts[i] = fromNs(startNsQ);
+      slices.ends[i] = fromNs(endNsQ);
+      slices.depths[i] = it.row.layoutDepth;
+      slices.titles[i] = internString(it.row.name);
+      slices.sliceIds[i] = it.row.id;
+      slices.isInstant[i] = it.row.isInstant;
+      slices.isIncomplete[i] = it.row.isIncomplete;
+      slices.colors![i] = greenIndex;
     }
     return slices;
   }
diff --git a/ui/src/tracks/process_scheduling/controller.ts b/ui/src/tracks/process_scheduling/controller.ts
index c7676ed..687d16d 100644
--- a/ui/src/tracks/process_scheduling/controller.ts
+++ b/ui/src/tracks/process_scheduling/controller.ts
@@ -134,7 +134,7 @@
                            `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
     const queryTable = isCached ? this.tableName('process_sched_cached') :
                                   this.tableName('process_sched');
-    const constainColumn = isCached ? 'cached_tsq' : 'ts';
+    const constraintColumn = isCached ? 'cached_tsq' : 'ts';
     return this.query(`
       select
         ${tsq} as tsq,
@@ -144,8 +144,8 @@
         utid
       from ${queryTable}
       where
-        ${constainColumn} >= ${startNs - this.maxDurNs} and
-        ${constainColumn} <= ${endNs}
+        ${constraintColumn} >= ${startNs - this.maxDurNs} and
+        ${constraintColumn} <= ${endNs}
       group by tsq, cpu
       order by tsq, cpu
     `);