Merge "TracedValue: add dchecks that the scope is always correct."
diff --git a/Android.bp b/Android.bp
index a8a334e..7c34f7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3559,6 +3559,7 @@
     "protos/perfetto/metrics/android/display_metrics.proto",
     "protos/perfetto/metrics/android/gpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+    "protos/perfetto/metrics/android/hwcomposer.proto",
     "protos/perfetto/metrics/android/hwui_metric.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/java_heap_histogram.proto",
@@ -3599,6 +3600,7 @@
     "protos/perfetto/metrics/android/display_metrics.proto",
     "protos/perfetto/metrics/android/gpu_metric.proto",
     "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+    "protos/perfetto/metrics/android/hwcomposer.proto",
     "protos/perfetto/metrics/android/hwui_metric.proto",
     "protos/perfetto/metrics/android/ion_metric.proto",
     "protos/perfetto/metrics/android/java_heap_histogram.proto",
@@ -7517,6 +7519,7 @@
     "src/trace_processor/metrics/android/android_cpu_agg.sql",
     "src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql",
     "src/trace_processor/metrics/android/android_gpu.sql",
+    "src/trace_processor/metrics/android/android_hwcomposer.sql",
     "src/trace_processor/metrics/android/android_hwui_metric.sql",
     "src/trace_processor/metrics/android/android_ion.sql",
     "src/trace_processor/metrics/android/android_lmk.sql",
@@ -7533,6 +7536,7 @@
     "src/trace_processor/metrics/android/android_task_names.sql",
     "src/trace_processor/metrics/android/android_task_state.sql",
     "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
+    "src/trace_processor/metrics/android/composition_layers.sql",
     "src/trace_processor/metrics/android/cpu_info.sql",
     "src/trace_processor/metrics/android/display_metrics.sql",
     "src/trace_processor/metrics/android/frame_missed.sql",
diff --git a/BUILD b/BUILD
index 256f5f5..19a8781 100644
--- a/BUILD
+++ b/BUILD
@@ -885,6 +885,7 @@
         "src/trace_processor/metrics/android/android_cpu_agg.sql",
         "src/trace_processor/metrics/android/android_cpu_raw_metrics_per_core.sql",
         "src/trace_processor/metrics/android/android_gpu.sql",
+        "src/trace_processor/metrics/android/android_hwcomposer.sql",
         "src/trace_processor/metrics/android/android_hwui_metric.sql",
         "src/trace_processor/metrics/android/android_ion.sql",
         "src/trace_processor/metrics/android/android_lmk.sql",
@@ -901,6 +902,7 @@
         "src/trace_processor/metrics/android/android_task_names.sql",
         "src/trace_processor/metrics/android/android_task_state.sql",
         "src/trace_processor/metrics/android/android_thread_time_in_state.sql",
+        "src/trace_processor/metrics/android/composition_layers.sql",
         "src/trace_processor/metrics/android/cpu_info.sql",
         "src/trace_processor/metrics/android/display_metrics.sql",
         "src/trace_processor/metrics/android/frame_missed.sql",
@@ -2281,6 +2283,7 @@
         "protos/perfetto/metrics/android/display_metrics.proto",
         "protos/perfetto/metrics/android/gpu_metric.proto",
         "protos/perfetto/metrics/android/heap_profile_callsites.proto",
+        "protos/perfetto/metrics/android/hwcomposer.proto",
         "protos/perfetto/metrics/android/hwui_metric.proto",
         "protos/perfetto/metrics/android/ion_metric.proto",
         "protos/perfetto/metrics/android/java_heap_histogram.proto",
diff --git a/BUILD.gn b/BUILD.gn
index 146ea00..278a678 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -135,8 +135,9 @@
     "protos/perfetto/common:cpp",
 
     # The diff testing framework depends on these descriptors.
-    "protos/perfetto/metrics:descriptor($host_toolchain)",
-    "protos/perfetto/trace:descriptor($host_toolchain)",
+    "protos/perfetto/metrics:descriptor",
+    "protos/perfetto/trace:descriptor",
+    "protos/perfetto/trace:test_extensions_descriptor",
 
     # Used in the when updating the ftrace protos
     "protos/perfetto/trace/ftrace:descriptor",
diff --git a/debian/changelog b/debian/changelog
index 40a8df5..fb48257 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,8 @@
+perfetto (11.0-1) unstable; urgency=medium
+  * Add the Perfetto perf sampling and profiling service.
+
+ -- Sami Kyostila <skyostil@google.com>  Mon, 18 Jan 2021 12:10:00 +0000
+
 perfetto (9.0-1) unstable; urgency=medium
 
   * Run traced under a dedicated system user account and set socket
diff --git a/debian/perfetto.install b/debian/perfetto.install
index ded0fdc..1ae73aa 100644
--- a/debian/perfetto.install
+++ b/debian/perfetto.install
@@ -1,6 +1,8 @@
 out/release/libperfetto.so usr/lib
 out/release/traced usr/sbin
+out/release/traced_perf usr/sbin
 out/release/traced_probes usr/sbin
 out/release/perfetto usr/bin
 debian/traced.service lib/systemd/system
+debian/traced-perf.service lib/systemd/system
 debian/traced-probes.service lib/systemd/system
diff --git a/debian/traced-perf.service b/debian/traced-perf.service
new file mode 100644
index 0000000..b04463b
--- /dev/null
+++ b/debian/traced-perf.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Perfetto sampling and profiling data source
+
+[Service]
+ExecStart=/usr/sbin/traced_perf
+User=root
+Group=root
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 82eb6cb..de8f70b 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -25,6 +25,7 @@
     "display_metrics.proto",
     "gpu_metric.proto",
     "heap_profile_callsites.proto",
+    "hwcomposer.proto",
     "hwui_metric.proto",
     "ion_metric.proto",
     "java_heap_histogram.proto",
diff --git a/protos/perfetto/metrics/android/hwcomposer.proto b/protos/perfetto/metrics/android/hwcomposer.proto
new file mode 100644
index 0000000..22644ce
--- /dev/null
+++ b/protos/perfetto/metrics/android/hwcomposer.proto
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message AndroidHwcomposerMetrics {
+  // Counts the number of composition total layers in the trace. (non-weighted average)
+  optional double composition_total_layers = 1;
+
+  // Counts the number of composition dpu layers in the trace. (non-weighted average)
+  optional double composition_dpu_layers = 2;
+
+  // Counts the number of composition gpu layers in the trace. (non-weighted average)
+  optional double composition_gpu_layers = 3;
+
+  // Counts the number of composition dpu cached layers in the trace. (non-weighted average)
+  optional double composition_dpu_cached_layers = 4;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index 244629c..8986b58 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -23,6 +23,7 @@
 import "protos/perfetto/metrics/android/display_metrics.proto";
 import "protos/perfetto/metrics/android/gpu_metric.proto";
 import "protos/perfetto/metrics/android/heap_profile_callsites.proto";
+import "protos/perfetto/metrics/android/hwcomposer.proto";
 import "protos/perfetto/metrics/android/hwui_metric.proto";
 import "protos/perfetto/metrics/android/ion_metric.proto";
 import "protos/perfetto/metrics/android/java_heap_histogram.proto";
@@ -55,7 +56,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 28
+// Next id: 29
 message TraceMetrics {
   reserved 4, 10, 13, 14, 19;
 
@@ -124,6 +125,9 @@
   // Frame timing and jank root causes for system UI interactions.
   optional AndroidSysUiCujMetrics android_sysui_cuj = 27;
 
+  // Metric associated with hwcomposer.
+  optional AndroidHwcomposerMetrics android_hwcomposer = 28;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 2d3e51f..36ac53a 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -251,6 +251,24 @@
 
 // End of protos/perfetto/metrics/android/heap_profile_callsites.proto
 
+// Begin of protos/perfetto/metrics/android/hwcomposer.proto
+
+message AndroidHwcomposerMetrics {
+  // Counts the number of composition total layers in the trace. (non-weighted average)
+  optional double composition_total_layers = 1;
+
+  // Counts the number of composition dpu layers in the trace. (non-weighted average)
+  optional double composition_dpu_layers = 2;
+
+  // Counts the number of composition gpu layers in the trace. (non-weighted average)
+  optional double composition_gpu_layers = 3;
+
+  // Counts the number of composition dpu cached layers in the trace. (non-weighted average)
+  optional double composition_dpu_cached_layers = 4;
+}
+
+// End of protos/perfetto/metrics/android/hwcomposer.proto
+
 // Begin of protos/perfetto/metrics/android/hwui_metric.proto
 
 // Android HWUI graphics performance and graphics memory usage metrics.
@@ -811,7 +829,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 28
+// Next id: 29
 message TraceMetrics {
   reserved 4, 10, 13, 14, 19;
 
@@ -880,6 +898,9 @@
   // Frame timing and jank root causes for system UI interactions.
   optional AndroidSysUiCujMetrics android_sysui_cuj = 27;
 
+  // Metric associated with hwcomposer.
+  optional AndroidHwcomposerMetrics android_hwcomposer = 28;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 0c39d63..9c195e4 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -111,7 +111,7 @@
   perfetto_proto_library("descriptor") {
     proto_generators = [ "descriptor" ]
     generate_descriptor = "trace.descriptor"
-    sources = [ "perfetto_trace.proto" ]
+    sources = [ "trace.proto" ]
   }
 }
 
@@ -121,3 +121,10 @@
   complete_static_lib = true
   deps = [ ":lite" ]
 }
+
+perfetto_proto_library("test_extensions_descriptor") {
+  proto_generators = [ "descriptor" ]
+  generate_descriptor = "test_extensions.descriptor"
+  sources = [ "test_extensions.proto" ]
+  deps = [ "track_event:source_set" ]
+}
diff --git a/protos/perfetto/trace/android/frame_timeline_event.proto b/protos/perfetto/trace/android/frame_timeline_event.proto
index c6c4482..86386a4 100644
--- a/protos/perfetto/trace/android/frame_timeline_event.proto
+++ b/protos/perfetto/trace/android/frame_timeline_event.proto
@@ -28,17 +28,19 @@
 // This relationship can be reconstructed by using
 //    DisplayFrame.token = SurfaceFrame.display_frame_token
 message FrameTimelineEvent {
-  // Specifies which component was the main source for the jank.
+  // Specifies the reason(s) most likely to have caused the jank.
+  // Used as a bitmask.
   enum JankType {
     JANK_UNSPECIFIED = 0;
     JANK_NONE = 1;
     JANK_SF_SCHEDULING = 2;
-    JANK_PREDICTION_ERROR = 3;
-    JANK_DISPLAY_HAL = 4;
-    JANK_SF_DEADLINE_MISSED = 5;
-    JANK_APP_DEADLINE_MISSED = 6;
-    JANK_BUFFER_STUFFING = 7;
-    JANK_UNKNOWN = 8;
+    JANK_PREDICTION_ERROR = 4;
+    JANK_DISPLAY_HAL = 8;
+    JANK_SF_CPU_DEADLINE_MISSED = 16;
+    JANK_SF_GPU_DEADLINE_MISSED = 32;
+    JANK_APP_DEADLINE_MISSED = 64;
+    JANK_BUFFER_STUFFING = 128;
+    JANK_UNKNOWN = 256;
   };
 
   // Specifies how a frame was presented on screen w.r.t. timing.
@@ -102,7 +104,9 @@
     optional PresentType present_type = 6;
     optional bool on_time_finish = 7;
     optional bool gpu_composition = 8;
-    optional JankType jank_type = 9;
+    // A bitmask of JankType. More than one reason can be attributed to a janky
+    // frame.
+    optional int32 jank_type = 9;
   };
 
   // Indicates the start of expected timeline slice for DisplayFrames.
@@ -144,7 +148,9 @@
     optional PresentType present_type = 4;
     optional bool on_time_finish = 5;
     optional bool gpu_composition = 6;
-    optional JankType jank_type = 7;
+    // A bitmask of JankType. More than one reason can be attributed to a janky
+    // frame.
+    optional int32 jank_type = 7;
   };
 
   // FrameEnd just sends the cookie to indicate that the corresponding
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 134c682..7e30a40 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -1854,17 +1854,19 @@
 // This relationship can be reconstructed by using
 //    DisplayFrame.token = SurfaceFrame.display_frame_token
 message FrameTimelineEvent {
-  // Specifies which component was the main source for the jank.
+  // Specifies the reason(s) most likely to have caused the jank.
+  // Used as a bitmask.
   enum JankType {
     JANK_UNSPECIFIED = 0;
     JANK_NONE = 1;
     JANK_SF_SCHEDULING = 2;
-    JANK_PREDICTION_ERROR = 3;
-    JANK_DISPLAY_HAL = 4;
-    JANK_SF_DEADLINE_MISSED = 5;
-    JANK_APP_DEADLINE_MISSED = 6;
-    JANK_BUFFER_STUFFING = 7;
-    JANK_UNKNOWN = 8;
+    JANK_PREDICTION_ERROR = 4;
+    JANK_DISPLAY_HAL = 8;
+    JANK_SF_CPU_DEADLINE_MISSED = 16;
+    JANK_SF_GPU_DEADLINE_MISSED = 32;
+    JANK_APP_DEADLINE_MISSED = 64;
+    JANK_BUFFER_STUFFING = 128;
+    JANK_UNKNOWN = 256;
   };
 
   // Specifies how a frame was presented on screen w.r.t. timing.
@@ -1928,7 +1930,9 @@
     optional PresentType present_type = 6;
     optional bool on_time_finish = 7;
     optional bool gpu_composition = 8;
-    optional JankType jank_type = 9;
+    // A bitmask of JankType. More than one reason can be attributed to a janky
+    // frame.
+    optional int32 jank_type = 9;
   };
 
   // Indicates the start of expected timeline slice for DisplayFrames.
@@ -1970,7 +1974,9 @@
     optional PresentType present_type = 4;
     optional bool on_time_finish = 5;
     optional bool gpu_composition = 6;
-    optional JankType jank_type = 7;
+    // A bitmask of JankType. More than one reason can be attributed to a janky
+    // frame.
+    optional int32 jank_type = 7;
   };
 
   // FrameEnd just sends the cookie to indicate that the corresponding
@@ -8386,24 +8392,3 @@
 }
 
 // End of protos/perfetto/trace/trace.proto
-
-// Begin of protos/perfetto/trace/test_extensions.proto
-
-// Extensions for TrackEvent used for integration testing. This proto file is
-// compiled to descriptor and is used in tools/diff_test_trace_processor.py.
-//
-// See docs/design-docs/extensions.md for more details.
-message TestExtension {
-  extend TrackEvent {
-    optional string string_extension_for_testing = 9900;
-    repeated int32 int_extension_for_testing = 9901;
-    optional string omitted_extension_for_testing = 9902;
-    optional TestExtensionChild nested_message_extension_for_testing = 9903;
-  }
-}
-
-message TestExtensionChild {
-  optional string child_field_for_testing = 1;
-}
-
-// End of protos/perfetto/trace/test_extensions.proto
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
index e1c9082..b98f940 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
@@ -44,25 +44,42 @@
 
 using FrameEndDecoder = protos::pbzero::FrameTimelineEvent_FrameEnd_Decoder;
 
+static StringId JankTypeBitmaskToStringId(TraceProcessorContext* context,
+                                          int32_t jank_type) {
+  if (jank_type == FrameTimelineEvent::JANK_UNSPECIFIED)
+    return context->storage->InternString("Unspecified");
+  if (jank_type == FrameTimelineEvent::JANK_NONE)
+    return context->storage->InternString("None");
+
+  std::vector<std::string> jank_reasons;
+  if (jank_type & FrameTimelineEvent::JANK_SF_SCHEDULING)
+    jank_reasons.emplace_back("SurfaceFlinger Scheduling");
+  if (jank_type & FrameTimelineEvent::JANK_PREDICTION_ERROR)
+    jank_reasons.emplace_back("Prediction Error");
+  if (jank_type & FrameTimelineEvent::JANK_DISPLAY_HAL)
+    jank_reasons.emplace_back("Display HAL");
+  if (jank_type & FrameTimelineEvent::JANK_SF_CPU_DEADLINE_MISSED)
+    jank_reasons.emplace_back("SurfaceFlinger CPU Deadline Missed");
+  if (jank_type & FrameTimelineEvent::JANK_SF_GPU_DEADLINE_MISSED)
+    jank_reasons.emplace_back("SurfaceFlinger GPU Deadline Missed");
+  if (jank_type & FrameTimelineEvent::JANK_APP_DEADLINE_MISSED)
+    jank_reasons.emplace_back("App Deadline Missed");
+  if (jank_type & FrameTimelineEvent::JANK_BUFFER_STUFFING)
+    jank_reasons.emplace_back("Buffer Stuffing");
+  if (jank_type & FrameTimelineEvent::JANK_UNKNOWN)
+    jank_reasons.emplace_back("Unknown jank");
+
+  std::string jank_str(
+      std::accumulate(jank_reasons.begin(), jank_reasons.end(), std::string(),
+                      [](const std::string& l, const std::string& r) {
+                        return l.empty() ? r : l + ", " + r;
+                      }));
+  return context->storage->InternString(base::StringView(jank_str));
+}
+
 FrameTimelineEventParser::FrameTimelineEventParser(
     TraceProcessorContext* context)
     : context_(context),
-      jank_type_ids_{
-          {context->storage->InternString(
-               "Unspecified Jank") /* JANK_UNSPECIFIED */,
-           context->storage->InternString("No Jank") /* JANK_NONE */,
-           context->storage->InternString(
-               "SurfaceFlinger Scheduling") /* JANK_SF_SCHEDULING */,
-           context->storage->InternString(
-               "Prediction Error") /* JANK_PREDICTION_ERROR */,
-           context->storage->InternString("Display HAL") /* JANK_DISPLAY_HAL */,
-           context->storage->InternString(
-               "SurfaceFlinger Deadline Missed") /*JANK_SF_DEADLINE_MISSED */,
-           context->storage->InternString(
-               "App Deadline Missed") /* JANK_APP_DEADLINE_MISSED */,
-           context->storage->InternString(
-               "Buffer Stuffing") /* JANK_BUFFER_STUFFING */,
-           context->storage->InternString("Unknown Jank") /* JANK_UNKNOWN */}},
       present_type_ids_{
           {context->storage->InternString(
                "Unspecified Present") /* PRESENT_UNSPECIFIED */,
@@ -167,7 +184,7 @@
       present_type_ids_[static_cast<size_t>(event.present_type())];
   actual_row.on_time_finish = event.on_time_finish();
   actual_row.gpu_composition = event.gpu_composition();
-  actual_row.jank_type = jank_type_ids_[static_cast<size_t>(event.jank_type())];
+  actual_row.jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
   context_->slice_tracker->BeginFrameTimeline(actual_row);
 }
 
@@ -289,7 +306,7 @@
       present_type_ids_[static_cast<size_t>(event.present_type())];
   actual_row.on_time_finish = event.on_time_finish();
   actual_row.gpu_composition = event.gpu_composition();
-  actual_row.jank_type = jank_type_ids_[static_cast<size_t>(event.jank_type())];
+  actual_row.jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
   context_->slice_tracker->BeginFrameTimeline(actual_row);
 }
 
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.h b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
index 73affb8..fedfdb5 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.h
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
@@ -29,6 +29,9 @@
 
 namespace trace_processor {
 
+using FrameTimelineEvent = protos::pbzero::FrameTimelineEvent;
+using FrameTimelineEventDecoder = protos::pbzero::FrameTimelineEvent_Decoder;
+
 class TraceProcessorContext;
 
 // Class for parsing graphics frame related events.
@@ -41,8 +44,6 @@
   void ParseFrameTimelineEvent(int64_t timestamp, ConstBytes);
 
  private:
-  using FrameTimelineEvent = protos::pbzero::FrameTimelineEvent;
-  using FrameTimelineEventDecoder = protos::pbzero::FrameTimelineEvent_Decoder;
   void ParseExpectedDisplayFrameStart(int64_t timestamp, ConstBytes);
   void ParseActualDisplayFrameStart(int64_t timestamp, ConstBytes);
 
@@ -59,7 +60,6 @@
   // of the cookie makes it so that we can end a slice with just the cookie and
   // the TrackSetId.
   std::map<int64_t, TrackSetId> cookie_track_set_id_map_;
-  std::array<StringId, 9> jank_type_ids_;
   std::array<StringId, 5> present_type_ids_;
   StringId expected_timeline_track_name_;
   StringId actual_timeline_track_name_;
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 0017914..b7bef7b 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -26,6 +26,7 @@
   "android/android_mem.sql",
   "android/android_mem_unagg.sql",
   "android/android_ion.sql",
+  "android/composition_layers.sql",
   "android/frame_missed.sql",
   "android/android_lmk_reason.sql",
   "android/android_lmk.sql",
@@ -41,6 +42,7 @@
   "android/display_metrics.sql",
   "android/heap_profile_callsites.sql",
   "android/hsc_startups.sql",
+  "android/android_hwcomposer.sql",
   "android/android_hwui_metric.sql",
   "android/java_heap_histogram.sql",
   "android/java_heap_stats.sql",
diff --git a/src/trace_processor/metrics/android/android_hwcomposer.sql b/src/trace_processor/metrics/android/android_hwcomposer.sql
new file mode 100644
index 0000000..769c659
--- /dev/null
+++ b/src/trace_processor/metrics/android/android_hwcomposer.sql
@@ -0,0 +1,50 @@
+--
+-- Copyright 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
+--
+--     https://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.
+
+SELECT RUN_METRIC(
+  'android/composition_layers.sql',
+  'track_name', 'HWComposer: Total Layer',
+  'output', 'total_layers'
+);
+
+SELECT RUN_METRIC(
+  'android/composition_layers.sql',
+  'track_name', 'HWComposer: DPU Layer',
+  'output', 'dpu_layers'
+);
+
+SELECT RUN_METRIC(
+  'android/composition_layers.sql',
+  'track_name', 'HWComposer: GPU Layer',
+  'output', 'gpu_layers'
+);
+
+SELECT RUN_METRIC(
+  'android/composition_layers.sql',
+  'track_name', 'HWComposer: Cached Layer',
+  'output', 'cached_layers'
+);
+
+CREATE VIEW android_hwcomposer_output AS
+SELECT AndroidHwcomposerMetrics(
+    'composition_total_layers', (SELECT AVG(value)
+                            FROM total_layers),
+    'composition_dpu_layers', (SELECT AVG(value)
+                            FROM dpu_layers),
+    'composition_gpu_layers', (SELECT AVG(value)
+                            FROM gpu_layers),
+    'composition_dpu_cached_layers', (SELECT AVG(value)
+                            FROM cached_layers)
+);
diff --git a/src/trace_processor/metrics/android/composition_layers.sql b/src/trace_processor/metrics/android/composition_layers.sql
new file mode 100644
index 0000000..f63a6d3
--- /dev/null
+++ b/src/trace_processor/metrics/android/composition_layers.sql
@@ -0,0 +1,29 @@
+--
+-- Copyright 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
+--
+--     https://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.
+
+CREATE VIEW IF NOT EXISTS {{output}} AS
+WITH composition_layer_counts AS (
+  SELECT
+    LAG(ts) OVER (ORDER BY ts) AS ts,
+    value
+  FROM counter c
+  JOIN process_counter_track t ON c.track_id = t.id
+  WHERE t.name = '{{track_name}}'
+)
+SELECT
+  ts,
+  value
+FROM composition_layer_counts
+WHERE value >= 0 AND ts IS NOT NULL;
diff --git a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
index c973e4c..659de3c 100644
--- a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
+++ b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor.sha1 b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor.sha1
index 427c6ca..dd4358f 100644
--- a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor.sha1
+++ b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor.sha1
@@ -2,4 +2,4 @@
 // SHA1(tools/gen_binary_descriptors)
 // 70978e4b6e0d773dd222715b1c7e74c25d344da0
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// 88089959b7be61227a18d46e91e635df4fca535c
+// e0033c91fc2ae9cd9730bbf5684f5661c16e4d5c
diff --git a/test/synth_common.py b/test/synth_common.py
index 222a338..baa4143 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -16,7 +16,7 @@
 import argparse
 
 from collections import namedtuple
-from google.protobuf import descriptor, descriptor_pb2, message_factory
+from google.protobuf import descriptor, descriptor_pb2, message_factory, descriptor_pool
 
 CLONE_THREAD = 0x00010000
 CLONE_VFORK = 0x00004000
@@ -672,34 +672,37 @@
     event = packet.frame_timeline_event.frame_end
     event.cookie = cookie
 
+
+def read_descriptor(file_name):
+  with open(file_name, 'rb') as f:
+    contents = f.read()
+
+  descriptor = descriptor_pb2.FileDescriptorSet()
+  descriptor.MergeFromString(contents)
+
+  return descriptor
+
+
+def create_pool(args):
+  trace_descriptor = read_descriptor(args.trace_descriptor)
+
+  pool = descriptor_pool.DescriptorPool()
+  for file in trace_descriptor.file:
+    pool.Add(file)
+
+  return pool
+
+
 def create_trace():
   parser = argparse.ArgumentParser()
   parser.add_argument(
       'trace_descriptor', type=str, help='location of trace descriptor')
   args = parser.parse_args()
 
-  with open(args.trace_descriptor, 'rb') as t:
-    fileContent = t.read()
-
-  file_desc_set_pb2 = descriptor_pb2.FileDescriptorSet()
-  file_desc_set_pb2.MergeFromString(fileContent)
-
-  desc_by_path = {}
-  for f_desc_pb2 in file_desc_set_pb2.file:
-    f_desc_pb2_encode = f_desc_pb2.SerializeToString()
-    f_desc = descriptor.FileDescriptor(
-        name=f_desc_pb2.name,
-        package=f_desc_pb2.package,
-        serialized_pb=f_desc_pb2_encode)
-
-    for desc in f_desc.message_types_by_name.values():
-      desc_by_path[desc.full_name] = desc
-
-    for desc in f_desc.enum_types_by_name.values():
-      desc_by_path[desc.full_name] = desc
-
-  factory = message_factory.MessageFactory()
-  ProtoTrace = factory.GetPrototype(desc_by_path['perfetto.protos.Trace'])
+  pool = create_pool(args)
+  factory = message_factory.MessageFactory(pool)
+  ProtoTrace = factory.GetPrototype(
+      pool.FindMessageTypeByName('perfetto.protos.Trace'))
 
   class EnumPrototype(object):
 
@@ -719,14 +722,15 @@
   ])
   prototypes = Prototypes(
       TrackEvent=factory.GetPrototype(
-          desc_by_path['perfetto.protos.TrackEvent']),
+          pool.FindMessageTypeByName('perfetto.protos.TrackEvent')),
       ChromeRAILMode=EnumPrototype.from_descriptor(
-          desc_by_path['perfetto.protos.ChromeRAILMode']),
+          pool.FindEnumTypeByName('perfetto.protos.ChromeRAILMode')),
       ThreadDescriptor=factory.GetPrototype(
-          desc_by_path['perfetto.protos.ThreadDescriptor']),
+          pool.FindMessageTypeByName('perfetto.protos.ThreadDescriptor')),
       ChromeProcessDescriptor=factory.GetPrototype(
-          desc_by_path['perfetto.protos.ChromeProcessDescriptor']),
+          pool.FindMessageTypeByName(
+              'perfetto.protos.ChromeProcessDescriptor')),
       CounterDescriptor=factory.GetPrototype(
-          desc_by_path['perfetto.protos.CounterDescriptor']),
+          pool.FindMessageTypeByName('perfetto.protos.CounterDescriptor')),
   )
   return Trace(ProtoTrace(), prototypes)
diff --git a/test/trace_processor/graphics/actual_frame_timeline_events.out b/test/trace_processor/graphics/actual_frame_timeline_events.out
index 0776aa8..a28b3c4 100644
--- a/test/trace_processor/graphics/actual_frame_timeline_events.out
+++ b/test/trace_processor/graphics/actual_frame_timeline_events.out
@@ -1,8 +1,9 @@
 "ts","dur","pid","display_frame_token","surface_frame_token","layer_name","present_type","on_time_finish","gpu_composition","jank_type"
-20,6,666,2,0,"[NULL]","On-time Present",1,0,"No Jank"
-21,16,1000,4,1,"Layer1","On-time Present",1,0,"No Jank"
+20,6,666,2,0,"[NULL]","On-time Present",1,0,"None"
+21,16,1000,4,1,"Layer1","On-time Present",1,0,"None"
 41,33,1000,6,5,"Layer1","Late Present",0,0,"App Deadline Missed"
-42,5,666,4,0,"[NULL]","On-time Present",1,0,"No Jank"
-81,7,666,6,0,"[NULL]","On-time Present",1,0,"No Jank"
+42,5,666,4,0,"[NULL]","On-time Present",1,0,"None"
+81,7,666,6,0,"[NULL]","On-time Present",1,0,"None"
 90,16,1000,8,7,"Layer1","Early Present",1,0,"SurfaceFlinger Scheduling"
 108,4,666,8,0,"[NULL]","Early Present",1,0,"SurfaceFlinger Scheduling"
+148,8,666,12,0,"[NULL]","Late Present",0,0,"SurfaceFlinger Scheduling, SurfaceFlinger CPU Deadline Missed"
diff --git a/test/trace_processor/graphics/composition_layer.py b/test/trace_processor/graphics/composition_layer.py
new file mode 100644
index 0000000..f25aec6
--- /dev/null
+++ b/test/trace_processor/graphics/composition_layer.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# 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.
+
+# This synthetic trace tests handling of the mm_id field in the rss_stat
+# event when mm_structs are reused on process death.
+
+from os import sys, path
+
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_packet(ts=1)
+trace.add_process(10, 1, "parent_process")
+trace.add_process(11, 10, "child_process")
+
+trace.add_ftrace_packet(1)
+
+trace.add_print(ts=99, tid=11, buf='C|10|HWComposer: Total Layer|7')
+trace.add_print(ts=100, tid=11, buf='C|10|HWComposer: Total Layer|5')
+trace.add_print(ts=101, tid=11, buf='C|10|HWComposer: Total Layer|6')
+trace.add_print(ts=102, tid=11, buf='C|10|HWComposer: Total Layer|0')
+trace.add_print(ts=103, tid=11, buf='C|10|HWComposer: Total Layer|4')
+trace.add_print(ts=104, tid=11, buf='C|10|HWComposer: Total Layer|6')
+trace.add_print(ts=105, tid=11, buf='C|10|HWComposer: Total Layer|0')
+trace.add_print(ts=106, tid=11, buf='C|10|HWComposer: Total Layer|4')
+trace.add_print(ts=107, tid=11, buf='C|10|HWComposer: Total Layer|5')
+trace.add_print(ts=108, tid=11, buf='C|10|HWComposer: Total Layer|0')
+trace.add_print(ts=108, tid=11, buf='C|10|HWComposer: Total Layer|3')
+trace.add_print(ts=108, tid=11, buf='C|10|HWComposer: Total Layer|0')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/composition_layer_count.out b/test/trace_processor/graphics/composition_layer_count.out
new file mode 100644
index 0000000..6c47b9e
--- /dev/null
+++ b/test/trace_processor/graphics/composition_layer_count.out
@@ -0,0 +1,3 @@
+
+"AVG(value)"
+3.000000
diff --git a/test/trace_processor/graphics/composition_layer_count.sql b/test/trace_processor/graphics/composition_layer_count.sql
new file mode 100644
index 0000000..30a92e6
--- /dev/null
+++ b/test/trace_processor/graphics/composition_layer_count.sql
@@ -0,0 +1,19 @@
+--
+-- Copyright 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
+--
+--     https://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.
+
+SELECT RUN_METRIC('android/android_hwcomposer.sql') AS suppress_query_output;
+
+SELECT AVG(value)
+FROM total_layers;
diff --git a/test/trace_processor/graphics/expected_frame_timeline_events.out b/test/trace_processor/graphics/expected_frame_timeline_events.out
index d695295..935d497 100644
--- a/test/trace_processor/graphics/expected_frame_timeline_events.out
+++ b/test/trace_processor/graphics/expected_frame_timeline_events.out
@@ -6,3 +6,4 @@
 80,6,666,6,0,"[NULL]"
 90,16,1000,8,7,"Layer1"
 120,6,666,8,0,"[NULL]"
+140,6,666,12,0,"[NULL]"
diff --git a/test/trace_processor/graphics/frame_timeline_events.py b/test/trace_processor/graphics/frame_timeline_events.py
index 78496b9..0acb485 100644
--- a/test/trace_processor/graphics/frame_timeline_events.py
+++ b/test/trace_processor/graphics/frame_timeline_events.py
@@ -21,12 +21,13 @@
   JANK_UNSPECIFIED = 0;
   JANK_NONE = 1;
   JANK_SF_SCHEDULING = 2;
-  JANK_PREDICTION_ERROR = 3;
-  JANK_DISPLAY_HAL = 4;
-  JANK_SF_DEADLINE_MISSED = 5;
-  JANK_APP_DEADLINE_MISSED = 6;
-  JANK_BUFFER_STUFFING = 7;
-  JANK_UNKNOWN = 8;
+  JANK_PREDICTION_ERROR = 4;
+  JANK_DISPLAY_HAL = 8;
+  JANK_SF_CPU_DEADLINE_MISSED = 16;
+  JANK_SF_GPU_DEADLINE_MISSED = 32;
+  JANK_APP_DEADLINE_MISSED = 64;
+  JANK_BUFFER_STUFFING = 128;
+  JANK_UNKNOWN = 256;
 
 class PresentType:
   PRESENT_UNSPECIFIED = 0;
@@ -75,4 +76,10 @@
 trace.add_actual_surface_frame_start_event(ts=90, cookie=14, token=7, display_frame_token=8, pid=1000, layer_name="Layer1", present_type=PresentType.PRESENT_EARLY, on_time_finish=1, gpu_composition=0, jank_type=JankType.JANK_SF_SCHEDULING)
 trace.add_frame_end_event(ts=106, cookie=14)
 
+# DisplayFrame with multiple jank reasons
+trace.add_expected_display_frame_start_event(ts=140, cookie=15, token=12, pid=666)
+trace.add_frame_end_event(ts=146, cookie=15)
+trace.add_actual_display_frame_start_event(ts=148, cookie=16, token=12, pid=666, present_type=PresentType.PRESENT_LATE, on_time_finish=0, gpu_composition=0, jank_type=JankType.JANK_SF_CPU_DEADLINE_MISSED | JankType.JANK_SF_SCHEDULING)
+trace.add_frame_end_event(ts=156, cookie=16)
+
 sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/index b/test/trace_processor/graphics/index
index c6e21c0..256e72d 100644
--- a/test/trace_processor/graphics/index
+++ b/test/trace_processor/graphics/index
@@ -29,3 +29,6 @@
 # Frame Timeline event trace tests
 frame_timeline_events.py expected_frame_timeline_events.sql expected_frame_timeline_events.out
 frame_timeline_events.py actual_frame_timeline_events.sql actual_frame_timeline_events.out
+
+# Composition layer
+composition_layer.py composition_layer_count.sql composition_layer_count.out
diff --git a/test/trace_processor/track_event/track_event_typed_args.textproto b/test/trace_processor/track_event/track_event_typed_args.textproto
index fdbfe2b..471f16a 100644
--- a/test/trace_processor/track_event/track_event_typed_args.textproto
+++ b/test/trace_processor/track_event/track_event_typed_args.textproto
@@ -125,3 +125,14 @@
     }
   }
 }
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 6000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    name: "name5"
+    type: 3
+    [perfetto.protos.ChromeTrackEvent.chrome_app_state]: APP_STATE_FOREGROUND
+  }
+}
diff --git a/test/trace_processor/track_event/track_event_typed_args_args.out b/test/trace_processor/track_event/track_event_typed_args_args.out
index 70411b2..229d3cd 100644
--- a/test/trace_processor/track_event/track_event_typed_args_args.out
+++ b/test/trace_processor/track_event/track_event_typed_args_args.out
@@ -15,3 +15,4 @@
 "int_extension_for_testing","int_extension_for_testing[1]",1337,"[NULL]"
 "nested_message_extension_for_testing.child_field_for_testing","nested_message_extension_for_testing.child_field_for_testing","[NULL]","nesting test"
 "string_extension_for_testing","string_extension_for_testing","[NULL]","an extension string!"
+"chrome_app_state","chrome_app_state","[NULL]","APP_STATE_FOREGROUND"
diff --git a/test/trace_processor/track_event/track_event_typed_args_slices.out b/test/trace_processor/track_event/track_event_typed_args_slices.out
index 7259082..85b697a 100644
--- a/test/trace_processor/track_event/track_event_typed_args_slices.out
+++ b/test/trace_processor/track_event/track_event_typed_args_slices.out
@@ -3,3 +3,4 @@
 "[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","name2"
 "[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","name3"
 "[NULL]","[NULL]","t1","[NULL]",4000,0,"cat","name4"
+"[NULL]","[NULL]","t1","[NULL]",6000,0,"cat","name5"
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index ccc3119..2565ca3 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -160,7 +160,8 @@
 
 
 def run_all_tests(trace_processor, trace_descriptor_path,
-                  metrics_message_factory, tests, keep_input, rebase):
+                  extension_descriptor_paths, metrics_message_factory, tests,
+                  keep_input, rebase):
   perf_data = []
   test_failure = 0
   rebased = 0
@@ -184,7 +185,8 @@
       gen_trace_path = os.path.realpath(gen_trace_file.name)
     elif trace_path.endswith('.textproto'):
       gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
-      serialize_textproto_trace(trace_descriptor_path, trace_path,
+      serialize_textproto_trace(trace_descriptor_path,
+                                extension_descriptor_paths, trace_path,
                                 gen_trace_file)
       gen_trace_path = os.path.realpath(gen_trace_file.name)
     else:
@@ -375,11 +377,10 @@
   tests = read_all_tests(query_metric_pattern, trace_pattern)
   sys.stderr.write('[==========] Running {} tests.\n'.format(len(tests)))
 
+  out_path = os.path.dirname(args.trace_processor)
   if args.trace_descriptor:
     trace_descriptor_path = args.trace_descriptor
   else:
-    out_path = os.path.dirname(args.trace_processor)
-
     def find_trace_descriptor(parent):
       trace_protos_path = os.path.join(parent, 'gen', 'protos', 'perfetto',
                                        'trace')
@@ -393,21 +394,24 @@
   if args.metrics_descriptor:
     metrics_descriptor_path = args.metrics_descriptor
   else:
-    out_path = os.path.dirname(args.trace_processor)
     metrics_protos_path = os.path.join(out_path, 'gen', 'protos', 'perfetto',
                                        'metrics')
     metrics_descriptor_path = os.path.join(metrics_protos_path,
                                            'metrics.descriptor')
 
+  chrome_extensions = os.path.join(out_path, 'gen', 'protos', 'third_party',
+                                   'chromium', 'chrome_track_event.descriptor')
+  test_extensions = os.path.join(out_path, 'gen', 'protos', 'perfetto', 'trace',
+                                 'test_extensions.descriptor')
+
   metrics_message_factory = create_metrics_message_factory(
       metrics_descriptor_path)
 
   test_run_start = datetime.datetime.now()
-  test_failure, perf_data, rebased = run_all_tests(args.trace_processor,
-                                                   trace_descriptor_path,
-                                                   metrics_message_factory,
-                                                   tests, args.keep_input,
-                                                   args.rebase)
+  test_failure, perf_data, rebased = run_all_tests(
+      args.trace_processor, trace_descriptor_path,
+      [chrome_extensions, test_extensions], metrics_message_factory, tests,
+      args.keep_input, args.rebase)
   test_run_end = datetime.datetime.now()
 
   sys.stderr.write('[==========] {} tests ran. ({} ms total)\n'.format(
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 15b2ecb..07964b6 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -32,7 +32,6 @@
 
 TRACE_PROTO_ROOTS = CONFIG_PROTO_ROOTS + [
   'protos/perfetto/trace/trace.proto',
-  'protos/perfetto/trace/test_extensions.proto',
 ]
 MERGED_TRACE_PROTO = 'protos/perfetto/trace/perfetto_trace.proto'
 
diff --git a/tools/print_descriptor.py b/tools/print_descriptor.py
new file mode 100755
index 0000000..e4c0a92
--- /dev/null
+++ b/tools/print_descriptor.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# 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.
+
+import argparse
+from google.protobuf import descriptor_pb2
+
+
+# Take a path to file with binary protobuf descriptor as CLI argument and print
+# it in textproto format.
+#
+# Example usage:
+#   tools/print_descriptor.py path/to/file.descriptor
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      'input_file',
+      type=str,
+      help='File name with binary proto descriptor to print')
+  args = parser.parse_args()
+
+  descriptor = descriptor_pb2.FileDescriptorSet()
+  with open(args.input_file, 'rb') as f:
+    contents = f.read()
+    descriptor.MergeFromString(contents)
+
+  print(descriptor)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/tools/proto_utils.py b/tools/proto_utils.py
index 455f15e..210b13c 100644
--- a/tools/proto_utils.py
+++ b/tools/proto_utils.py
@@ -18,7 +18,7 @@
 import subprocess
 import tempfile
 
-from google.protobuf import descriptor, descriptor_pb2, message_factory
+from google.protobuf import descriptor, descriptor_pb2, message_factory, descriptor_pool
 from google.protobuf import reflection, text_format
 
 
@@ -46,11 +46,31 @@
   return message_factory.MessageFactory().GetPrototype(desc_by_path[proto_type])
 
 
-def serialize_textproto_trace(trace_descriptor_path, text_proto_path,
-                              out_stream):
-  trace_message_factory = create_message_factory(trace_descriptor_path,
-                                                 'perfetto.protos.Trace')
-  proto = trace_message_factory()
+def read_descriptor(file_name):
+  with open(file_name, 'rb') as f:
+    contents = f.read()
+
+  descriptor = descriptor_pb2.FileDescriptorSet()
+  descriptor.MergeFromString(contents)
+
+  return descriptor
+
+
+def serialize_textproto_trace(trace_descriptor_path, extension_descriptor_paths,
+                              text_proto_path, out_stream):
+  pool = descriptor_pool.DescriptorPool()
+  trace_descriptor = read_descriptor(trace_descriptor_path)
+  for file in trace_descriptor.file:
+    pool.Add(file)
+
+  for path in extension_descriptor_paths:
+    descriptor = read_descriptor(path)
+    for file in descriptor.file:
+      pool.Add(file)
+
+  proto = message_factory.MessageFactory().GetPrototype(
+      pool.FindMessageTypeByName('perfetto.protos.Trace'))()
+
   with open(text_proto_path, 'r') as text_proto_file:
     text_format.Merge(text_proto_file.read(), proto)
   out_stream.write(proto.SerializeToString())
@@ -59,7 +79,11 @@
 
 def serialize_python_trace(trace_descriptor_path, python_trace_path,
                            out_stream):
-  python_cmd = ['python3', python_trace_path, trace_descriptor_path]
+  python_cmd = [
+      'python3',
+      python_trace_path,
+      trace_descriptor_path,
+  ]
 
   # Add the test dir to the PYTHONPATH to allow synth_common to be found.
   env = os.environ.copy()
diff --git a/tools/serialize_test_trace.py b/tools/serialize_test_trace.py
index cf8257f..5bb9080 100755
--- a/tools/serialize_test_trace.py
+++ b/tools/serialize_test_trace.py
@@ -21,7 +21,7 @@
 import os
 import sys
 
-from proto_utils import create_message_factory, serialize_python_trace, serialize_textproto_trace
+from proto_utils import serialize_python_trace, serialize_textproto_trace
 
 
 def main():
@@ -38,19 +38,29 @@
   if args.out and not args.descriptor:
     trace_protos_path = os.path.join(args.out, 'gen', 'protos', 'perfetto',
                                      'trace')
+    chrome_extension_descriptor_path = os.path.join(
+        args.out, 'gen', 'protos', 'third_party', 'chromium',
+        'chrome_track_event.descriptor')
     trace_descriptor_path = os.path.join(trace_protos_path, 'trace.descriptor')
+    test_extensions_descriptor_path = os.path.join(
+        trace_protos_path, 'test_extensions.descriptor')
+    extension_descriptors = [
+        chrome_extension_descriptor_path, test_extensions_descriptor_path
+    ]
   elif args.descriptor and not args.out:
     trace_descriptor_path = args.descriptor
+    extension_descriptors = []
   else:
-    raise RuntimeError('Exactly one of --out and --descriptor should be provided')
+    raise RuntimeError(
+        'Exactly one of --out and --descriptor should be provided')
 
   trace_path = args.trace_path
 
   if trace_path.endswith('.py'):
     serialize_python_trace(trace_descriptor_path, trace_path, sys.stdout.buffer)
   elif trace_path.endswith('.textproto'):
-    serialize_textproto_trace(trace_descriptor_path, trace_path,
-                              sys.stdout.buffer)
+    serialize_textproto_trace(trace_descriptor_path, extension_descriptors,
+                              trace_path, sys.stdout.buffer)
   else:
     raise RuntimeError('Invalid extension for unserialized trace file')
 
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index 5a5d261..a394e2b 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -16,6 +16,7 @@
 
 #include "tools/trace_to_text/trace_to_profile.h"
 
+#include <random>
 #include <string>
 #include <vector>
 
@@ -49,6 +50,17 @@
 namespace trace_to_text {
 namespace {
 
+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);
+  }
+  return result;
+}
+
 void MaybeSymbolize(trace_processor::TraceProcessor* tp) {
   std::unique_ptr<profiling::Symbolizer> symbolizer =
       profiling::LocalSymbolizerOrDie(profiling::GetPerfettoBinaryPath(),
@@ -99,8 +111,8 @@
     return 0;
   }
 
-  std::string temp_dir =
-      GetTemp() + "/" + dirname_prefix + base::GetTimeFmt("%y%m%d%H%M%S");
+  std::string temp_dir = GetTemp() + "/" + dirname_prefix +
+                         base::GetTimeFmt("%y%m%d%H%M%S") + GetRandomString(5);
   PERFETTO_CHECK(base::Mkdir(temp_dir));
   for (const auto& profile : profiles) {
     std::string filename = temp_dir + "/" + filename_fn(profile);
diff --git a/ui/src/controller/aggregation/cpu_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
index 70a643b..a8b1161 100644
--- a/ui/src/controller/aggregation/cpu_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
@@ -54,7 +54,7 @@
   }
 
   getTabName() {
-    return 'CPU Slices';
+    return 'CPU by thread';
   }
 
   async getExtra() {}
diff --git a/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
new file mode 100644
index 0000000..baf9efe
--- /dev/null
+++ b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
@@ -0,0 +1,101 @@
+// 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.
+
+import {ColumnDef} from '../../common/aggregation_data';
+import {Engine} from '../../common/engine';
+import {Area, Sorting} from '../../common/state';
+import {toNs} from '../../common/time';
+import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices/common';
+import {globals} from '../globals';
+
+import {AggregationController} from './aggregation_controller';
+
+export class CpuByProcessAggregationController extends AggregationController {
+  async createAggregateView(engine: Engine, area: Area) {
+    await engine.query(`drop view if exists ${this.kind};`);
+
+    const selectedCpus = [];
+    for (const trackId of area.tracks) {
+      const track = globals.state.tracks[trackId];
+      // Track will be undefined for track groups.
+      if (track !== undefined && track.kind === CPU_SLICE_TRACK_KIND) {
+        selectedCpus.push((track.config as Config).cpu);
+      }
+    }
+    if (selectedCpus.length === 0) return false;
+
+    const query = `create view ${this.kind} as
+        SELECT process.name as process_name, pid,
+        sum(dur) AS total_dur,
+        sum(dur)/count(1) as avg_dur,
+        count(1) as occurrences
+        FROM process
+        JOIN thread USING(upid)
+        JOIN thread_state USING(utid)
+        WHERE cpu IN (${selectedCpus}) AND
+        state = "Running" AND
+        thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
+        thread_state.ts < ${toNs(area.endSec)} group by upid`;
+
+    await engine.query(query);
+    return true;
+  }
+
+  getTabName() {
+    return 'CPU by process';
+  }
+
+  async getExtra() {}
+
+  getDefaultSorting(): Sorting {
+    return {column: 'total_dur', direction: 'DESC'};
+  }
+
+  getColumnDefinitions(): ColumnDef[] {
+    return [
+      {
+        title: 'Process',
+        kind: 'STRING',
+        columnConstructor: Uint16Array,
+        columnId: 'process_name',
+      },
+      {
+        title: 'PID',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'pid'
+      },
+      {
+        title: 'Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'total_dur',
+        sum: true
+      },
+      {
+        title: 'Avg Wall duration (ms)',
+        kind: 'TIMESTAMP_NS',
+        columnConstructor: Float64Array,
+        columnId: 'avg_dur'
+      },
+      {
+        title: 'Occurrences',
+        kind: 'NUMBER',
+        columnConstructor: Uint16Array,
+        columnId: 'occurrences',
+        sum: true
+      }
+    ];
+  }
+}
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 29f8709..ab061c7 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -39,6 +39,9 @@
   CpuAggregationController
 } from './aggregation/cpu_aggregation_controller';
 import {
+  CpuByProcessAggregationController
+} from './aggregation/cpu_by_process_aggregation_controller';
+import {
   SliceAggregationController
 } from './aggregation/slice_aggregation_controller';
 import {
@@ -173,6 +176,10 @@
             ThreadAggregationController,
             {engine, kind: 'thread_state_aggregation'}));
         childControllers.push(Child(
+            'cpu_process_aggregation',
+            CpuByProcessAggregationController,
+            {engine, kind: 'cpu_by_process_aggregation'}));
+        childControllers.push(Child(
             'slice_aggregation',
             SliceAggregationController,
             {engine, kind: 'slice_aggregation'}));