Merge "TraceProcessor: add android_dumpstate table and ingestion"
diff --git a/Android.bp b/Android.bp
index 56f9d47..7b7a14e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -9221,6 +9221,7 @@
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
diff --git a/BUILD b/BUILD
index c66d42e..a7e008d 100644
--- a/BUILD
+++ b/BUILD
@@ -1261,6 +1261,7 @@
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
diff --git a/gn/standalone/toolchain/win_find_msvc.py b/gn/standalone/toolchain/win_find_msvc.py
index 0badd1b..179ae79 100644
--- a/gn/standalone/toolchain/win_find_msvc.py
+++ b/gn/standalone/toolchain/win_find_msvc.py
@@ -63,16 +63,19 @@
     filt = lambda x: os.path.exists(os.path.join(x, 'ucrt', 'x64', 'ucrt.lib'))
     out[1] = find_max_subdir(lib_base, filt)
 
-  for version in ['BuildTools', 'Community', 'Professional']:
-    msvc_base = ('C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\'
-                 '{}\\VC\\Tools\\MSVC').format(version)
-    if os.path.exists(msvc_base):
-      filt = lambda x: os.path.exists(
-          os.path.join(x, 'lib', 'x64', 'libcmt.lib'))
-      max_msvc = find_max_subdir(msvc_base, filt)
-      if max_msvc is not None:
-        out[2] = os.path.join(msvc_base, max_msvc)
-      break
+  for year in ['2022', '2021', '2020', '2019']:
+    for version in [
+        'BuildTools', 'Community', 'Professional', 'Enterprise', 'Preview'
+    ]:
+      msvc_base = ('C:\\Program Files (x86)\\Microsoft Visual Studio\\'
+                   f'{year}\\{version}\\VC\\Tools\\MSVC')
+      if os.path.exists(msvc_base):
+        filt = lambda x: os.path.exists(
+            os.path.join(x, 'lib', 'x64', 'libcmt.lib'))
+        max_msvc = find_max_subdir(msvc_base, filt)
+        if max_msvc is not None:
+          out[2] = os.path.join(msvc_base, max_msvc)
+        break
 
   # Don't error in case of failure, GN scripts are supposed to deal with
   # failures and allow the user to override the dirs.
diff --git a/include/perfetto/tracing/event_context.h b/include/perfetto/tracing/event_context.h
index 9250a51..e88c89c 100644
--- a/include/perfetto/tracing/event_context.h
+++ b/include/perfetto/tracing/event_context.h
@@ -110,11 +110,11 @@
   // values directly to TRACE_EVENT (i.e. TRACE_EVENT(..., "arg", value, ...);)
   // but in rare cases (e.g. when an argument should be written conditionally)
   // EventContext::AddDebugAnnotation provides an explicit equivalent.
-  template <typename T>
-  void AddDebugAnnotation(const char* name, T&& value) {
+  template <typename EventNameType, typename T>
+  void AddDebugAnnotation(EventNameType&& name, T&& value) {
     if (tls_state_ && tls_state_->filter_debug_annotations)
       return;
-    auto annotation = AddDebugAnnotation(name);
+    auto annotation = AddDebugAnnotation(std::forward<EventNameType>(name));
     WriteIntoTracedValue(internal::CreateTracedValueFromProto(annotation, this),
                          std::forward<T>(value));
   }
@@ -133,6 +133,8 @@
   EventContext(const EventContext&) = delete;
 
   protos::pbzero::DebugAnnotation* AddDebugAnnotation(const char* name);
+  protos::pbzero::DebugAnnotation* AddDebugAnnotation(
+      ::perfetto::DynamicString name);
 
   TracePacketHandle trace_packet_;
   protos::pbzero::TrackEvent* event_;
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 00aa3f5..f777528 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -219,14 +219,15 @@
 
   // TODO(altimin): Remove this method once Chrome uses
   // EventContext::AddDebugAnnotation directly.
-  template <typename T>
+  template <typename NameType, typename ValueType>
   static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
-                                 const char* name,
-                                 T&& value) {
-    auto annotation = AddDebugAnnotation(event_ctx, name);
+                                 NameType&& name,
+                                 ValueType&& value) {
+    auto annotation =
+        AddDebugAnnotation(event_ctx, std::forward<NameType>(name));
     WriteIntoTracedValue(
         internal::CreateTracedValueFromProto(annotation, event_ctx),
-        std::forward<T>(value));
+        std::forward<ValueType>(value));
   }
 
   // If the given track hasn't been seen by the trace writer yet, write a
@@ -291,10 +292,15 @@
       TraceTimestamp,
       uint32_t seq_flags =
           protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+
   static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
       perfetto::EventContext*,
       const char* name);
 
+  static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
+      perfetto::EventContext*,
+      perfetto::DynamicString name);
+
   static std::atomic<int> session_count_;
 };
 
diff --git a/include/perfetto/tracing/internal/write_track_event_args.h b/include/perfetto/tracing/internal/write_track_event_args.h
index ba6c4e8..f54c4ab 100644
--- a/include/perfetto/tracing/internal/write_track_event_args.h
+++ b/include/perfetto/tracing/internal/write_track_event_args.h
@@ -149,6 +149,16 @@
   WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
 }
 
+// Write one debug annotation and recursively write the rest of the arguments.
+template <typename ArgValue, typename... Args>
+PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
+                                                DynamicString arg_name,
+                                                ArgValue&& arg_value,
+                                                Args&&... args) {
+  event_ctx.AddDebugAnnotation(arg_name, std::forward<ArgValue>(arg_value));
+  WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
+}
+
 }  // namespace internal
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
index 2501c71..6ff7815 100644
--- a/include/perfetto/tracing/track_event_legacy.h
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -97,8 +97,6 @@
 static constexpr char TRACE_EVENT_PHASE_MEMORY_DUMP = 'v';
 static constexpr char TRACE_EVENT_PHASE_MARK = 'R';
 static constexpr char TRACE_EVENT_PHASE_CLOCK_SYNC = 'c';
-static constexpr char TRACE_EVENT_PHASE_ENTER_CONTEXT = '(';
-static constexpr char TRACE_EVENT_PHASE_LEAVE_CONTEXT = ')';
 
 // Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
 static constexpr uint32_t TRACE_EVENT_FLAG_NONE =
@@ -395,23 +393,16 @@
   // No arguments.
   static void AddDebugAnnotations(EventContext*) {}
 
-  // One argument.
-  template <typename ArgType>
+  // N number of debug arguments.
+  template <typename ArgNameType, typename ArgType, typename... OtherArgs>
   static void AddDebugAnnotations(EventContext* ctx,
-                                  const char* arg_name,
-                                  ArgType&& arg_value) {
-    TrackEventInternal::AddDebugAnnotation(ctx, arg_name, arg_value);
-  }
-
-  // Two arguments.
-  template <typename ArgType, typename ArgType2>
-  static void AddDebugAnnotations(EventContext* ctx,
-                                  const char* arg_name,
+                                  ArgNameType&& arg_name,
                                   ArgType&& arg_value,
-                                  const char* arg_name2,
-                                  ArgType2&& arg_value2) {
-    TrackEventInternal::AddDebugAnnotation(ctx, arg_name, arg_value);
-    TrackEventInternal::AddDebugAnnotation(ctx, arg_name2, arg_value2);
+                                  OtherArgs&&... more_args) {
+    TrackEventInternal::AddDebugAnnotation(ctx,
+                                           std::forward<ArgNameType>(arg_name),
+                                           std::forward<ArgType>(arg_value));
+    AddDebugAnnotations(ctx, std::forward<OtherArgs>(more_args)...);
   }
 
  private:
@@ -493,6 +484,7 @@
                                        thread_id, ...)                     \
   [&]() {                                                                  \
     using ::perfetto::internal::TrackEventInternal;                        \
+    PERFETTO_DCHECK(!(flags & TRACE_EVENT_FLAG_COPY));                     \
     /* First check the scope for instant events. */                        \
     if ((phase) == TRACE_EVENT_PHASE_INSTANT) {                            \
       /* Note: Avoids the need to set LegacyEvent::instant_event_scope. */ \
@@ -647,16 +639,17 @@
 #define TRACE_EVENT_COPY_INSTANT0(category_group, name, scope)        \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, \
                            ::perfetto::DynamicString{name}, scope)
-#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name,     \
-                                  arg1_val)                                   \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,         \
-                           ::perfetto::DynamicString{name}, scope, arg1_name, \
-                           arg1_val)
-#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name,     \
-                                  arg1_val, arg2_name, arg2_val)              \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,         \
-                           ::perfetto::DynamicString{name}, scope, arg1_name, \
-                           arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name, \
+                                  arg1_val)                               \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,     \
+                           ::perfetto::DynamicString{name}, scope,        \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name,  \
+                                  arg1_val, arg2_name, arg2_val)           \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,      \
+                           ::perfetto::DynamicString{name}, scope,         \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val, \
+                           ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_INSTANT_WITH_FLAGS0(category_group, name, scope_and_flags) \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
                            scope_and_flags)
@@ -700,8 +693,9 @@
                                 arg2_name, arg2_val)                       \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group,        \
                            ::perfetto::DynamicString{name},                \
-                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val,     \
-                           arg2_name, arg2_val)
+                           TRACE_EVENT_FLAG_NONE,                          \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val, \
+                           ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Begin events with explicit timestamps.
 #define TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \
@@ -720,14 +714,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
       TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                     \
       ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2(               \
-    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \
-    arg2_name, arg2_val)                                                 \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
-      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                     \
-      ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2(                   \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val,     \
+    arg2_name, arg2_val)                                                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                         \
+      ::perfetto::DynamicString{name}, id, thread_id, timestamp,             \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val, \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // End events.
 #define TRACE_EVENT_END0(category_group, name)                          \
@@ -751,7 +746,8 @@
                               arg2_name, arg2_val)                            \
   INTERNAL_TRACE_EVENT_ADD(                                                   \
       TRACE_EVENT_PHASE_END, category_group, ::perfetto::DynamicString{name}, \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val,  \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Mark events.
 #define TRACE_EVENT_MARK_WITH_TIMESTAMP0(category_group, name, timestamp)  \
@@ -776,10 +772,10 @@
                            ::perfetto::DynamicString{name},        \
                            TRACE_EVENT_FLAG_NONE)
 
-#define TRACE_EVENT_COPY_MARK1(category_group, name, arg1_name, arg1_val) \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_MARK, category_group,        \
-                           ::perfetto::DynamicString{name},               \
-                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_MARK1(category_group, name, arg1_name, arg1_val)      \
+  INTERNAL_TRACE_EVENT_ADD(                                                    \
+      TRACE_EVENT_PHASE_MARK, category_group, ::perfetto::DynamicString{name}, \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
 
 #define TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(category_group, name, timestamp)  \
   INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                     \
@@ -803,14 +799,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
       TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
       ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2(                 \
-    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \
-    arg2_name, arg2_val)                                                 \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
-      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
-      ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2(                     \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val,     \
+    arg2_name, arg2_val)                                                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                           \
+      ::perfetto::DynamicString{name}, id, thread_id, timestamp,             \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val, \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Counters.
 #define TRACE_COUNTER1(category_group, name, value)                         \
@@ -903,16 +900,17 @@
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
 #define TRACE_EVENT_COPY_ASYNC_BEGIN1(category_group, name, id, arg1_name, \
                                       arg1_val)                            \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,          \
-                                   category_group,                         \
-                                   ::perfetto::DynamicString{name}, id,    \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name,   \
-                                      arg1_val, arg2_name, arg2_val)         \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                         \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,          \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name, \
+                                      arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,          \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                      \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_ASYNC_BEGIN_WITH_FLAGS0(category_group, name, id, flags) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,            \
                                    category_group, name, id, flags)
@@ -1008,16 +1006,17 @@
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
 #define TRACE_EVENT_COPY_ASYNC_END1(category_group, name, id, arg1_name, \
                                     arg1_val)                            \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,          \
-                                   category_group,                       \
-                                   ::perfetto::DynamicString{name}, id,  \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name,     \
-                                    arg1_val, arg2_name, arg2_val)           \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_ASYNC_END, category_group,                           \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name, \
+                                    arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                    \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_ASYNC_END_WITH_FLAGS0(category_group, name, id, flags) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,            \
                                    category_group, name, id, flags)
@@ -1119,13 +1118,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,           \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_ASYNC_TTS,  \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2(                  \
     category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group,             \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_ASYNC_TTS,  \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Async events with explicit timestamps.
 #define TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, \
@@ -1165,18 +1166,19 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,            \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN1(category_group, name, id,   \
-                                               arg1_name, arg1_val)        \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \
-                                   category_group,                         \
-                                   ::perfetto::DynamicString{name}, id,    \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN2(                              \
-    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val)      \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,                \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN1(category_group, name, id, \
+                                               arg1_name, arg1_val)      \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,            \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN2(                         \
+    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,           \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,       \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END0(category_group, name, id) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                    \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group,            \
@@ -1192,7 +1194,8 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,                 \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_API_CURRENT_THREAD_ID, \
-      timestamp, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+      timestamp, TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, \
+      arg1_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(                  \
     category_group, name, id, timestamp)                                      \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
@@ -1205,7 +1208,8 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                      \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,      \
       TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                      \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Metadata events.
 #define TRACE_EVENT_METADATA1(category_group, name, arg1_name, arg1_val) \
@@ -1245,16 +1249,6 @@
                                    category_group, name, id,         \
                                    TRACE_EVENT_FLAG_NONE)
 
-// Context events.
-#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context)    \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ENTER_CONTEXT, \
-                                   category_group, name, context,   \
-                                   TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context)    \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_LEAVE_CONTEXT, \
-                                   category_group, name, context,   \
-                                   TRACE_EVENT_FLAG_NONE)
-
 // TODO(skyostil): Implement binary-efficient trace events.
 #define TRACE_EVENT_BINARY_EFFICIENT0 TRACE_EVENT0
 #define TRACE_EVENT_BINARY_EFFICIENT1 TRACE_EVENT1
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index f0b1658..bdcd051 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -11059,7 +11059,7 @@
 
 // Begin of protos/perfetto/trace/trace_packet.proto
 
-// TracePacket is the root object of a Perfeto trace.
+// TracePacket is the root object of a Perfetto trace.
 // A Perfetto trace is a linear sequence of TracePacket(s).
 //
 // The tracing service guarantees that all TracePacket(s) written by a given
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 53732c8..e195bcc 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -70,7 +70,7 @@
 
 package perfetto.protos;
 
-// TracePacket is the root object of a Perfeto trace.
+// TracePacket is the root object of a Perfetto trace.
 // A Perfetto trace is a linear sequence of TracePacket(s).
 //
 // The tracing service guarantees that all TracePacket(s) written by a given
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index c342de0..de9f40e 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -1762,10 +1762,6 @@
   EXPECT_GT(total_allocated, 0u);
 }
 
-// Disable these tests when running with sanitizers. They (double) fork and that
-// seems to cause flaky crashes with sanitizers.
-#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) && \
-    !defined(MEMORY_SANITIZER) && !defined(LEAK_SANITIZER)
 // On in-tree Android, we use the system heapprofd in fork or central mode.
 // For Linux and out-of-tree Android, we statically include a copy of
 // heapprofd and use that. This one does not support intercepting malloc.
@@ -1787,10 +1783,6 @@
            std::make_tuple(TestMode::kCentral, AllocatorMode::kCustom)),
     TestSuffix);
 #endif
-#else  // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
-       // defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER)
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HeapprofdEndToEnd);
-#endif
 
 }  // namespace
 }  // namespace profiling
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index c9f00e6..b9da196 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -432,7 +432,7 @@
       }
 
       std::string key;
-      auto res = ReadOneJsonKey(start, end, &key, &next);
+      ReadKeyRes res = ReadOneJsonKey(start, end, &key, &next);
       if (res == ReadKeyRes::kFatalError)
         return util::ErrStatus("Failure parsing JSON: encountered fatal error");
 
@@ -447,9 +447,12 @@
       } else if (key == "systemTraceEvents") {
         position_ = TracePosition::kSystemTraceEventsString;
         return ParseInternal(next + 1, end, out);
+      } else if (key == "androidProcessDump") {
+        position_ = TracePosition::kAndroidProcessDumpString;
+        return ParseInternal(next + 1, end, out);
       } else if (key == "metadata") {
         position_ = TracePosition::kWaitingForMetadataDictionary;
-        return ParseInternal(next + 1, end, out);
+        return ParseInternal(next, end, out);
       } else if (key == "displayTimeUnit") {
         std::string time_unit;
         auto result = ReadOneJsonString(next + 1, end, &time_unit, &next);
@@ -517,7 +520,7 @@
       }
 
       base::StringView unparsed;
-      const auto res = ReadOneJsonDict(next, end, &unparsed, &next);
+      ReadDictRes res = ReadOneJsonDict(next, end, &unparsed, &next);
       if (res == ReadDictRes::kEndOfArray)
         return util::ErrStatus("Failure parsing JSON: encountered fatal error");
       if (res == ReadDictRes::kEndOfTrace ||
@@ -527,7 +530,27 @@
 
       // TODO(lalitm): read and ingest the relevant data inside |value|.
       position_ = TracePosition::kDictionaryKey;
-      break;
+      return ParseInternal(next, end, out);
+    }
+    case TracePosition::kAndroidProcessDumpString: {
+      if (format_ != TraceFormat::kOuterDictionary) {
+        return util::ErrStatus(
+            "Failure parsing JSON: illegal format when parsing metadata");
+      }
+
+      std::string unparsed;
+      ReadStringRes res = ReadOneJsonString(next, end, &unparsed, &next);
+      if (res == ReadStringRes::kNeedsMoreData) {
+        break;
+      }
+      if (res == ReadStringRes::kFatalError) {
+        return base::ErrStatus(
+            "Failure parsing JSON: illegal string when parsing "
+            "androidProcessDump");
+      }
+      // TODO(lalitm): read and ingest the relevant data inside |unparsed|.
+      position_ = TracePosition::kDictionaryKey;
+      return ParseInternal(next, end, out);
     }
     case TracePosition::kTraceEventsArray: {
       while (next < end) {
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.h b/src/trace_processor/importers/json/json_trace_tokenizer.h
index 8074b61..5e75611 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.h
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.h
@@ -127,6 +127,10 @@
     // This position is only valid when the |format_| == |kOuterDictionary|.
     kSystemTraceEventsString,
 
+    // This indicates we are inside the androidProcessDump string.
+    // This position is only valid when the |format_| == |kOuterDictionary|.
+    kAndroidProcessDumpString,
+
     // This indicates we are waiting for the entire metadata dictionary to be
     // available.
     kWaitingForMetadataDictionary,
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index 090d673..db1d194 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -178,6 +178,7 @@
     TraceProcessor* tp;
     std::vector<SqlMetricFile>* metrics;
   };
+  static constexpr bool kVoidReturn = true;
   static base::Status Run(Context* ctx,
                           size_t argc,
                           sqlite3_value** argv,
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index fcc81ba..13a3472 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -63,6 +63,7 @@
   "android/g2d_duration.sql",
   "android/g2d.sql",
   "android/global_counter_span_view.sql",
+  "android/global_counter_span_view_merged.sql",
   "android/gpu_counter_span_view.sql",
   "android/jank/cujs.sql",
   "android/jank/cujs_boundaries.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index 2ee4530..1584976 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -99,10 +99,18 @@
     AND dur != -1
 ;
 
-SELECT RUN_METRIC('android/global_counter_span_view.sql',
+SELECT RUN_METRIC('android/global_counter_span_view_merged.sql',
   'table_name', 'screen_state',
   'counter_name', 'ScreenState');
 
+SELECT RUN_METRIC('android/process_counter_span_view.sql',
+  'table_name', 'doze_light_state',
+  'counter_name', 'DozeLightState');
+
+SELECT RUN_METRIC('android/process_counter_span_view.sql',
+  'table_name', 'doze_deep_state',
+  'counter_name', 'DozeDeepState');
+
 DROP TABLE IF EXISTS screen_state_span_with_suspend;
 CREATE VIRTUAL TABLE screen_state_span_with_suspend
     USING span_join(screen_state_span, suspend_slice_);
@@ -115,10 +123,56 @@
        'Suspended' AS slice_name,
        'Suspend / resume' AS track_name,
        'slice' AS track_type
-FROM suspend_slice_;
+FROM suspend_slice_
+UNION ALL
+SELECT ts,
+       dur,
+       CASE screen_state_val
+         WHEN 1 THEN 'Screen off'
+         WHEN 2 THEN 'Screen on'
+         WHEN 3 THEN 'Always-on display (doze)'
+         ELSE 'unknown'
+       END AS slice_name,
+       'Screen state' AS track_name,
+       'slice' AS track_type
+FROM screen_state_span
+UNION ALL
+-- See DeviceIdleController.java for where these states come from and how
+-- they transition.
+SELECT ts,
+       dur,
+       CASE doze_light_state_val
+         WHEN 0 THEN 'active'
+         WHEN 1 THEN 'inactive'
+         WHEN 4 THEN 'idle'
+         WHEN 5 THEN 'waiting_for_network'
+         WHEN 6 THEN 'idle_maintenance'
+         WHEN 7 THEN 'override'
+         ELSE 'unknown'
+       END AS slice_name,
+       'Doze light state' AS track_name,
+       'slice' AS track_type
+FROM doze_light_state_span
+UNION ALL
+SELECT ts,
+       dur,
+       CASE doze_deep_state_val
+         WHEN 0 THEN 'active'
+         WHEN 1 THEN 'inactive'
+         WHEN 2 THEN 'idle_pending'
+         WHEN 3 THEN 'sensing'
+         WHEN 4 THEN 'locating'
+         WHEN 5 THEN 'idle'
+         WHEN 6 THEN 'idle_maintenance'
+         WHEN 7 THEN 'quick_doze_delay'
+         ELSE 'unknown'
+       END AS slice_name,
+       'Doze deep state' AS track_name,
+       'slice' AS track_type
+FROM doze_deep_state_span;
 
 DROP VIEW IF EXISTS android_batt_output;
-CREATE VIEW android_batt_output AS
+CREATE VIEW android_batt_output AS 
 SELECT AndroidBatteryMetric(
   'battery_counters', (
     SELECT RepeatedField(
diff --git a/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql b/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
new file mode 100644
index 0000000..668ff87
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
@@ -0,0 +1,31 @@
+--
+-- Copyright 2020 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.
+--
+
+DROP VIEW IF EXISTS {{table_name}}_span;
+CREATE VIEW {{table_name}}_span AS
+SELECT
+  ts,
+  LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
+      OVER(ORDER BY ts) - ts AS dur,
+  CAST(value AS INT) AS {{table_name}}_val
+FROM (
+    SELECT ts, value, LAG(value) OVER (ORDER BY ts) as lag_value
+    FROM counter c JOIN counter_track t
+      ON t.id = c.track_id
+    WHERE t.type = 'counter_track'
+      AND name = '{{counter_name}}'
+)
+WHERE value != lag_value OR lag_value IS NULL;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
index d57f8a4..a18c4ab 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
@@ -129,8 +129,10 @@
 
 -- |chrome_mojo_slices| is a view over |chrome_mojo_slices_internal| table, adding
 -- columns from |thread_slice| table.
-DROP VIEW IF EXISTS chrome_mojo_slices;
-CREATE VIEW chrome_mojo_slices AS
+-- TODO(243269096): switch this back to a view once we understand why rolling SQLite to
+-- 3.39.2 causes slowdowns.
+DROP TABLE IF EXISTS chrome_mojo_slices;
+CREATE TABLE chrome_mojo_slices AS
 SELECT
   s1.interface_name,
   s1.ipc_hash,
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
index dfab111..4f7e59c 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
@@ -17,7 +17,6 @@
 -- {{duration_causing_jank_ms}} : The duration of a single task that would cause
 -- jank, by delaying input from being handled on the main thread.
 
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
 SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
 
 -- Get the tasks that was running for more than 8ms within windows
diff --git a/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql b/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
index 426bd25..5c4357c 100644
--- a/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
+++ b/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('chrome/rail_modes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/rail_modes.sql');
 
 -- Creates a view cpu_time_by_rail_mode containing the CPU time used (across all
 -- cores) for each RAIL Mode slice in combined_overall_rail_slices.
diff --git a/src/trace_processor/metrics/sql/chrome/rail_modes.sql b/src/trace_processor/metrics/sql/chrome/rail_modes.sql
index d28f665..c693f08 100644
--- a/src/trace_processor/metrics/sql/chrome/rail_modes.sql
+++ b/src/trace_processor/metrics/sql/chrome/rail_modes.sql
@@ -14,8 +14,8 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('chrome/chrome_processes.sql') AS suppress_query_output;
-SELECT RUN_METRIC('chrome/chrome_event_metadata.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT RUN_METRIC('chrome/chrome_event_metadata.sql');
 
 -- Priority order for RAIL modes where response has the highest priority and
 -- idle has the lowest.
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
index 9b45876..5dd2df8 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
@@ -33,8 +33,10 @@
 -- Sort track ids to optimize joining with slices
 -- as engine doesn't do the sort to join in O(LogN)
 -- per row by default
-DROP VIEW IF EXISTS chrome_annotated_threads_and_processes;
-CREATE VIEW chrome_annotated_threads_and_processes AS
+-- TODO(243269096): switch this back to a view once we understand why rolling SQLite to
+-- 3.39.2 causes slowdowns.
+DROP TABLE IF EXISTS chrome_annotated_threads_and_processes;
+CREATE TABLE chrome_annotated_threads_and_processes AS
   SELECT
     thread_track.id AS track_id,
     chrome_thread.canonical_name AS thread_name,
@@ -51,8 +53,10 @@
 -- See b/166441398 & crbug/1094361 for why we remove threadpool (originally
 -- the -to-End step). In essence -to-End is often reported on the ThreadPool
 -- after the fact with explicit timestamps so it being blocked isn't noteworthy.
-DROP VIEW IF EXISTS blocking_chrome_tasks_without_threadpool;
-CREATE VIEW blocking_chrome_tasks_without_threadpool AS
+-- TODO(243269096): switch this back to a view once we understand why rolling SQLite to
+-- 3.39.2 causes slowdowns.
+DROP TABLE IF EXISTS blocking_chrome_tasks_without_threadpool;
+CREATE TABLE blocking_chrome_tasks_without_threadpool AS
   SELECT
      slice.*,
      annotations.thread_name AS thread_name,
diff --git a/src/trace_processor/rpc/query_result_serializer.cc b/src/trace_processor/rpc/query_result_serializer.cc
index d3b9ba2..eec9079 100644
--- a/src/trace_processor/rpc/query_result_serializer.cc
+++ b/src/trace_processor/rpc/query_result_serializer.cc
@@ -194,8 +194,10 @@
   strings = nullptr;
 
   // Write the cells headers (1 byte per cell).
-  batch->AppendBytes(BatchProto::kCellsFieldNumber, cell_types.data(),
-                     cell_idx);
+  if (cell_idx > 0) {
+    batch->AppendBytes(BatchProto::kCellsFieldNumber, cell_types.data(),
+                       cell_idx);
+  }
 
   // Append the |varint_cells|, copying over the packed varint buffer.
   if (varints.size())
@@ -234,7 +236,9 @@
   }  // if (doubles_size > 0)
 
   // Append the blobs.
-  batch->AppendRawProtoBytes(blobs.data(), blobs.size());
+  if (blobs.size() > 0) {
+    batch->AppendRawProtoBytes(blobs.data(), blobs.size());
+  }
 
   // If this is the last batch, write the EOF field.
   if (!batch_full) {
diff --git a/src/trace_processor/sqlite/create_function.h b/src/trace_processor/sqlite/create_function.h
index c6778c2..c0e1eb7 100644
--- a/src/trace_processor/sqlite/create_function.h
+++ b/src/trace_processor/sqlite/create_function.h
@@ -54,6 +54,8 @@
     State* state;
   };
 
+  static constexpr bool kVoidReturn = true;
+
   static base::Status Run(Context* ctx,
                           size_t argc,
                           sqlite3_value** argv,
diff --git a/src/trace_processor/sqlite/create_view_function.h b/src/trace_processor/sqlite/create_view_function.h
index af5615e..d23bfa0 100644
--- a/src/trace_processor/sqlite/create_view_function.h
+++ b/src/trace_processor/sqlite/create_view_function.h
@@ -33,6 +33,8 @@
     sqlite3* db;
   };
 
+  static constexpr bool kVoidReturn = true;
+
   static base::Status Run(Context* ctx,
                           size_t argc,
                           sqlite3_value** argv,
diff --git a/src/trace_processor/sqlite/register_function.h b/src/trace_processor/sqlite/register_function.h
index 95c24cd..aa33561 100644
--- a/src/trace_processor/sqlite/register_function.h
+++ b/src/trace_processor/sqlite/register_function.h
@@ -53,6 +53,15 @@
   // Can be redefined in any sub-classes to override the context.
   using Context = void;
 
+  // Indicates whether this function is "void" (i.e. doesn't actually want
+  // to return a value). While the function will still return null in SQL
+  // (because SQLite does not actually allow null functions), for accounting
+  // purposes, this null will be ignored when verifying whether this statement
+  // has any output.
+  // Can be redefined in any sub-classes to override it.
+  // If this is set to true, subclasses must not modify |out| or |destructors|.
+  static constexpr bool kVoidReturn = false;
+
   // Struct which holds destructors for strings/bytes returned from the
   // function. Passed as an argument to |Run| to allow implementations to
   // override the destructors.
@@ -158,8 +167,23 @@
     return;
   }
 
-  sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
-                               destructors.bytes_destructor);
+  if (Function::kVoidReturn) {
+    if (!value.is_null()) {
+      sqlite3_result_error(ctx, "void SQL function returned value", -1);
+      return;
+    }
+
+    // If the function doesn't want to return anything, set the "VOID"
+    // pointer type to a non-null value. Note that because of the weird
+    // way |sqlite3_value_pointer| works, we need to set some value even
+    // if we don't actually read it - just set it to a pointer to an empty
+    // string for this reason.
+    static char kVoidValue[] = "";
+    sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
+  } else {
+    sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
+                                 destructors.bytes_destructor);
+  }
 
   status = Function::VerifyPostConditions(ud);
   if (!status.ok()) {
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/sqlite/sqlite_raw_table.cc
index a42d83b..6a9921a 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.cc
+++ b/src/trace_processor/sqlite/sqlite_raw_table.cc
@@ -37,6 +37,7 @@
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/g2d.pbzero.h"
 #include "protos/perfetto/trace/ftrace/irq.pbzero.h"
+#include "protos/perfetto/trace/ftrace/mdss.pbzero.h"
 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
 #include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
@@ -430,6 +431,18 @@
     });
     writer_->AppendString("]");
     return;
+  } else if (event_name_ == "tracing_mark_write") {
+    using TMW = protos::pbzero::TracingMarkWriteFtraceEvent;
+    WriteValueForField(TMW::kTraceBeginFieldNumber,
+                       [this](const Variadic& value) {
+                         PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+                         writer_->AppendChar(value.uint_value ? 'B' : 'E');
+                       });
+    writer_->AppendString("|");
+    WriteValueForField(TMW::kPidFieldNumber, DVW());
+    writer_->AppendString("|");
+    WriteValueForField(TMW::kTraceNameFieldNumber, DVW());
+    return;
   } else if (event_name_ == "dpu_tracing_mark_write") {
     using TMW = protos::pbzero::DpuTracingMarkWriteFtraceEvent;
     WriteValueForField(TMW::kTypeFieldNumber, [this](const Variadic& value) {
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index f3bf43b..1e47d56 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -779,15 +779,26 @@
   if (sqlite_utils::IsStmtDone(stmt))
     return;
 
-  // If the statement only has a single column and that column is named
-  // "suppress_query_output", treat it as a statement without output for
-  // accounting purposes. This is done so that embedders (e.g. shell) can
-  // strictly check that only the last query produces output while also
-  // providing an escape hatch for SELECT RUN_METRIC() invocations (which
-  // sadly produce output).
-  if (sqlite3_column_count(stmt) == 1 &&
-      strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
-    return;
+  if (sqlite3_column_count(stmt) == 1) {
+    sqlite3_value* value = sqlite3_column_value(stmt, 0);
+
+    // If the "VOID" pointer associated to the return value is not null,
+    // that means this is a function which is forced to return a value
+    // (because all functions in SQLite have to) but doesn't actually
+    // wait to (i.e. it wants to be treated like CREATE TABLE or similar).
+    // Because of this, ignore the return value of this function.
+    // See |WrapSqlFunction| for where this is set.
+    if (sqlite3_value_pointer(value, "VOID") != nullptr) {
+      return;
+    }
+
+    // If the statement only has a single column and that column is named
+    // "suppress_query_output", treat it as a statement without output for
+    // accounting purposes. This allows an escape hatch for cases where the
+    // user explicitly wants to ignore functions as having output.
+    if (strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
+      return;
+    }
   }
 
   // Otherwise, the statement has output and so increment the count.
diff --git a/src/tracing/event_context.cc b/src/tracing/event_context.cc
index b73aee8..35981de 100644
--- a/src/tracing/event_context.cc
+++ b/src/tracing/event_context.cc
@@ -61,4 +61,11 @@
   return annotation;
 }
 
+protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
+    ::perfetto::DynamicString name) {
+  auto annotation = event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index d49b737..02bb577 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -515,5 +515,14 @@
   return annotation;
 }
 
+// static
+protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
+    perfetto::EventContext* event_ctx,
+    perfetto::DynamicString name) {
+  auto annotation = event_ctx->event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
+}
+
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index d1c4673..1a0ec66 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -749,8 +749,12 @@
           if (!first_annotation) {
             slice += ",";
           }
-          slice +=
-              incremental_state.GetDebugAnnotationName(it.name_iid()) + "=";
+          if (it.has_name_iid()) {
+            slice += incremental_state.GetDebugAnnotationName(it.name_iid());
+          } else {
+            slice += it.name();
+          }
+          slice += "=";
           std::stringstream value;
           if (it.has_bool_value()) {
             value << "(bool)" << it.bool_value();
@@ -3339,9 +3343,11 @@
                     perfetto::DynamicString(std::string("arg1_value6")));
   const char* value7 = "arg1_value7";
   TRACE_EVENT_BEGIN("foo", "Event7", "arg1", perfetto::StaticString(value7));
+  const char* arg_name = "new_arg1";
+  TRACE_EVENT_BEGIN("foo", "Event8", perfetto::DynamicString{arg_name}, 5);
 
   auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
-  ASSERT_EQ(7u, slices.size());
+  ASSERT_EQ(8u, slices.size());
   EXPECT_EQ("B:foo.Event1(arg1=(string)arg1_value1)", slices[0]);
   EXPECT_EQ("B:foo.Event2(arg1=(string)arg1_value2)", slices[1]);
   EXPECT_EQ("B:foo.Event3(arg1=(string)arg1_value3)", slices[2]);
@@ -3349,6 +3355,7 @@
   EXPECT_EQ("B:foo.Event5(arg1=(string)arg1_value5)", slices[4]);
   EXPECT_EQ("B:foo.Event6(arg1=(string)arg1_value6)", slices[5]);
   EXPECT_EQ("B:foo.Event7(arg1=(string)arg1_value7)", slices[6]);
+  EXPECT_EQ("B:foo.Event8(new_arg1=(int)5)", slices[7]);
 }
 
 TEST_P(PerfettoApiTest, FilterDynamicEventName) {
@@ -4271,14 +4278,16 @@
 }
 
 TEST_P(PerfettoApiTest, LegacyTraceEventsCopyDynamicString) {
-  char ptr1[] = "ABC";
-  char ptr2[] = "XYZ";
+  char ptr1[] = "A1";
+  char ptr2[] = "B1";
+  char arg_name1[] = "C1";
+  char arg_name2[] = "D1";
   auto* tracing_session = NewTraceWithCategories({"cat"});
   tracing_session->get()->StartBlocking();
   {
     TRACE_EVENT_MARK_WITH_TIMESTAMP0("cat", ptr1, MyTimestamp{0});
-    ptr1[0] = 'D';
-    // Old value of event name ("ABC") is recorded here in trace.
+    ptr1[1] = '3';
+    // Old value of event name ("A1") is recorded here in trace.
     // The reason being, in legacy macros, event name was expected to be static
     // by default unless `_COPY` version of these macro is used.
     // Perfetto is caching pointer values and if a event-name-pointer matches an
@@ -4289,14 +4298,34 @@
   }
   {
     TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP("cat", ptr2, MyTimestamp{0});
-    ptr2[0] = 'W';
+    ptr2[1] = '4';
     TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP("cat", ptr2, MyTimestamp{0});
   }
+  {
+    TRACE_EVENT_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE, arg_name1,
+                         /*arg_value=*/5);
+    arg_name1[1] = '5';
+    // Since we don't use the _COPY version here, this event will record the old
+    // value of arg_name1 (see earlier comment for full explanation).
+    TRACE_EVENT_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE, arg_name1,
+                         /*arg_value=*/5);
+  }
+  {
+    TRACE_EVENT_COPY_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE,
+                              arg_name2, /*arg_value=*/5);
+    arg_name2[1] = '6';
+    TRACE_EVENT_COPY_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE,
+                              arg_name2, /*arg_value=*/5);
+  }
   auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
   EXPECT_THAT(
       slices,
-      ElementsAre("[track=0]Legacy_R:cat.ABC", "[track=0]Legacy_R:cat.ABC",
-                  "[track=0]Legacy_R:cat.XYZ", "[track=0]Legacy_R:cat.WYZ"));
+      ElementsAre("[track=0]Legacy_R:cat.A1", "[track=0]Legacy_R:cat.A1",
+                  "[track=0]Legacy_R:cat.B1", "[track=0]Legacy_R:cat.B4",
+                  "[track=0]I:cat.event_name(C1=(int)5)",
+                  "[track=0]I:cat.event_name(C1=(int)5)",
+                  "[track=0]I:cat.event_name(D1=(int)5)",
+                  "[track=0]I:cat.event_name(D6=(int)5)"));
 }
 
 TEST_P(PerfettoApiTest, LegacyTraceEvents) {
diff --git a/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql
index 9690f17..b8ef699 100644
--- a/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql
+++ b/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql
@@ -11,7 +11,7 @@
 -- 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('chrome/chrome_processes.sql') AS suppress_query_output;
+-- SELECT RUN_METRIC('chrome/chrome_processes.sql');
 -- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql');
 SELECT * FROM real_power_by_rail_mode;
diff --git a/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql b/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql
index 372d8df..491df76 100644
--- a/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql
+++ b/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql
@@ -13,6 +13,6 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('experimental/chrome_dropped_frames.sql') AS suppress_query_output;
+SELECT RUN_METRIC('experimental/chrome_dropped_frames.sql');
 
 SELECT * FROM dropped_frames_with_process_info;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql b/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql
index c802094..0dfffaf 100644
--- a/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql
+++ b/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
 
 SELECT
   *
diff --git a/test/trace_processor/chrome/chrome_long_latency_metric_test.sql b/test/trace_processor/chrome/chrome_long_latency_metric_test.sql
index 13db1ff..80e6fb4 100644
--- a/test/trace_processor/chrome/chrome_long_latency_metric_test.sql
+++ b/test/trace_processor/chrome/chrome_long_latency_metric_test.sql
@@ -13,6 +13,6 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('experimental/chrome_long_latency.sql') AS suppress_query_output;
+SELECT RUN_METRIC('experimental/chrome_long_latency.sql');
 
 SELECT * FROM long_latency_with_process_info;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_processes_test.sql b/test/trace_processor/chrome/chrome_processes_test.sql
index 5ca4ded..c06f2bb 100644
--- a/test/trace_processor/chrome/chrome_processes_test.sql
+++ b/test/trace_processor/chrome/chrome_processes_test.sql
@@ -11,5 +11,5 @@
 -- 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('chrome/chrome_processes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
 SELECT pid, name, process_type FROM chrome_process;
diff --git a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql b/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql
index 6f8bc5a..41b1db6 100644
--- a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql
+++ b/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql
@@ -11,7 +11,7 @@
 -- 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('chrome/chrome_scroll_inputs_per_frame.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_scroll_inputs_per_frame.sql');
 
 SELECT
 count_for_frame,
diff --git a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql b/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql
index 4caea94..d331389 100644
--- a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql
+++ b/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql
@@ -15,7 +15,7 @@
 
 SELECT RUN_METRIC('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
 'dur_causes_jank_ms',
-/* dur_causes_jank_ms = */ '5') AS suppress_query_output;
+/* dur_causes_jank_ms = */ '5');
 
 SELECT
   full_name,
diff --git a/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql b/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql
index 3b33d47..cdb37a7 100644
--- a/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql
+++ b/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql
@@ -16,7 +16,7 @@
 SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
     'target_duration_ms', '0.000001',
     'thread_name', '"CrBrowserMain"',
-    'task_name', '"sendTouchEvent"') AS suppress_query_output;
+    'task_name', '"sendTouchEvent"');
 
 SELECT
     sample.description,
diff --git a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql b/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql
index d0b2a19..3935e88 100644
--- a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql
+++ b/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql
@@ -15,7 +15,7 @@
 
 SELECT RUN_METRIC('chrome/chrome_tasks_delaying_input_processing.sql',
 'duration_causing_jank_ms',
- /* duration_causing_jank_ms = */ '8') AS suppress_query_output;
+ /* duration_causing_jank_ms = */ '8');
 
 SELECT
   full_name,
diff --git a/test/trace_processor/chrome/chrome_tasks_test.sql b/test/trace_processor/chrome/chrome_tasks_test.sql
index 4e9d9f6..bb56ee9 100644
--- a/test/trace_processor/chrome/chrome_tasks_test.sql
+++ b/test/trace_processor/chrome/chrome_tasks_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/chrome_tasks.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_tasks.sql');
 
 SELECT full_name, task_type, count() as count
 FROM chrome_tasks
diff --git a/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql b/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql
index 124d306..0d4b22e 100644
--- a/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql
+++ b/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql
@@ -15,7 +15,7 @@
 --
 
 SELECT RUN_METRIC('chrome/chrome_thread_slice.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   name,
diff --git a/test/trace_processor/chrome/chrome_thread_slice_test.sql b/test/trace_processor/chrome/chrome_thread_slice_test.sql
index 654f424..cc4a8ec 100644
--- a/test/trace_processor/chrome/chrome_thread_slice_test.sql
+++ b/test/trace_processor/chrome/chrome_thread_slice_test.sql
@@ -15,7 +15,7 @@
 --
 
 SELECT RUN_METRIC('chrome/chrome_thread_slice.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
diff --git a/test/trace_processor/chrome/chrome_threads_test.sql b/test/trace_processor/chrome/chrome_threads_test.sql
index 8fb2839..f10e31f 100644
--- a/test/trace_processor/chrome/chrome_threads_test.sql
+++ b/test/trace_processor/chrome/chrome_threads_test.sql
@@ -11,7 +11,7 @@
 -- 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('chrome/chrome_processes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
 SELECT tid, name, is_main_thread, canonical_name
 FROM chrome_thread
 ORDER BY tid, name;
diff --git a/test/trace_processor/chrome/combined_rail_modes_test.sql b/test/trace_processor/chrome/combined_rail_modes_test.sql
index 5b49749..e3737b2 100644
--- a/test/trace_processor/chrome/combined_rail_modes_test.sql
+++ b/test/trace_processor/chrome/combined_rail_modes_test.sql
@@ -11,5 +11,5 @@
 -- 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('chrome/rail_modes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/rail_modes.sql');
 SELECT * FROM combined_overall_rail_slices;
diff --git a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql
index b66726a..2bfd9a8 100644
--- a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql
+++ b/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql
@@ -11,7 +11,7 @@
 -- 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('chrome/chrome_processes.sql') AS suppress_query_output;
+-- SELECT RUN_METRIC('chrome/chrome_processes.sql');
 -- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql');
 SELECT * FROM cpu_time_by_rail_mode;
diff --git a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql
index 7aab878..dd1af82 100644
--- a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql
+++ b/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql
@@ -11,7 +11,7 @@
 -- 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('chrome/chrome_processes.sql') AS suppress_query_output;
+-- SELECT RUN_METRIC('chrome/chrome_processes.sql');
 -- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql');
 SELECT * FROM power_by_rail_mode;
diff --git a/test/trace_processor/chrome/frame_times_metric_test.sql b/test/trace_processor/chrome/frame_times_metric_test.sql
index 28c0c42..0eb0ad1 100644
--- a/test/trace_processor/chrome/frame_times_metric_test.sql
+++ b/test/trace_processor/chrome/frame_times_metric_test.sql
@@ -13,6 +13,6 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('experimental/frame_times.sql') AS suppress_query_output;
+SELECT RUN_METRIC('experimental/frame_times.sql');
 
 SELECT * FROM AvgSurfaceFps;
diff --git a/test/trace_processor/chrome/modified_rail_modes_test.sql b/test/trace_processor/chrome/modified_rail_modes_test.sql
index 053aca8..83e8483 100644
--- a/test/trace_processor/chrome/modified_rail_modes_test.sql
+++ b/test/trace_processor/chrome/modified_rail_modes_test.sql
@@ -11,5 +11,5 @@
 -- 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('chrome/rail_modes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/rail_modes.sql');
 SELECT * FROM modified_rail_slices;
diff --git a/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql b/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql
index 053aca8..83e8483 100644
--- a/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql
+++ b/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql
@@ -11,5 +11,5 @@
 -- 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('chrome/rail_modes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/rail_modes.sql');
 SELECT * FROM modified_rail_slices;
diff --git a/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql b/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql
index f6ceab6..326b017 100644
--- a/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql
+++ b/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql
@@ -12,7 +12,7 @@
 -- 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('chrome/scroll_flow_event.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
 
 SELECT
   -- Each trace_id (in our example trace not true in general) has 8 steps. There
diff --git a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
index a8e78bf..2563840 100644
--- a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
+++ b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   -- Each trace_id (in our example trace not true in general) has 8 steps. There
diff --git a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql
index 50e79b8..3e00a31 100644
--- a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql
+++ b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 
 SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
+ ;
 
 -- trace 2956 is janky and 2954 and 2960 surround it (both are not janky). We
 -- just manually computed these values to ensure the queuing time is correct.
diff --git a/test/trace_processor/chrome/scroll_flow_event_test.sql b/test/trace_processor/chrome/scroll_flow_event_test.sql
index 00dbba6..f842e0d 100644
--- a/test/trace_processor/chrome/scroll_flow_event_test.sql
+++ b/test/trace_processor/chrome/scroll_flow_event_test.sql
@@ -12,7 +12,7 @@
 -- 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('chrome/scroll_flow_event.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
 
 SELECT
   trace_id,
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql
index 9441841..11df8a1 100644
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   COUNT(*) as total,
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql
index 7c8b59a..e36ff60 100644
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   process_name,
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql
index 4941383..ce8d3a6 100644
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql
@@ -12,8 +12,7 @@
 -- 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('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
 
 SELECT
   process_name,
diff --git a/test/trace_processor/chrome/scroll_jank_cause_test.sql b/test/trace_processor/chrome/scroll_jank_cause_test.sql
index 658ec22..6556b2d 100644
--- a/test/trace_processor/chrome/scroll_jank_cause_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_cause_test.sql
@@ -12,7 +12,7 @@
 -- 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('chrome/scroll_jank_cause.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_jank_cause.sql');
 
 SELECT
   COUNT(*) AS total,
diff --git a/test/trace_processor/chrome/scroll_jank_general_validation_test.sql b/test/trace_processor/chrome/scroll_jank_general_validation_test.sql
index 1aa3059..8792360 100644
--- a/test/trace_processor/chrome/scroll_jank_general_validation_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_general_validation_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/scroll_jank.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_jank.sql');
 
 SELECT (
   -- There are only two valid scrolls (one additional scroll is missing a begin
diff --git a/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql b/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql
index d3740b5..93171ed 100644
--- a/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/scroll_jank.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_jank.sql');
 
 SELECT ts, jank
 FROM scroll_jank
diff --git a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql b/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql
index 3b090e6..b346f2d 100644
--- a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
+   ;
 
 SELECT
   trace_id,
diff --git a/test/trace_processor/chrome/scroll_jank_test.sql b/test/trace_processor/chrome/scroll_jank_test.sql
index 9aaa99b..4ca71a8 100644
--- a/test/trace_processor/chrome/scroll_jank_test.sql
+++ b/test/trace_processor/chrome/scroll_jank_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/scroll_jank.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/scroll_jank.sql');
 
 SELECT
   gesture_scroll_id,
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql b/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql
index f1e9c33..d954797 100644
--- a/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql
+++ b/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 
 SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
+ ;
 
 -- trace 6911 is janky and 6915 and 6940 succeed it (both are not janky).
 SELECT
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql b/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql
index 0230908..bad05ff 100644
--- a/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql
+++ b/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 
 SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
+ ;
 
 -- trace 6911 is janky and 6915 and 6940 succeed it (both are not janky).
 SELECT
diff --git a/test/trace_processor/chrome/touch_flow_event_test.sql b/test/trace_processor/chrome/touch_flow_event_test.sql
index dbb5c89..4bb8399 100644
--- a/test/trace_processor/chrome/touch_flow_event_test.sql
+++ b/test/trace_processor/chrome/touch_flow_event_test.sql
@@ -12,7 +12,7 @@
 -- 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('chrome/touch_flow_event.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/touch_flow_event.sql');
 
 SELECT
   trace_id,
diff --git a/test/trace_processor/chrome/touch_jank_test.sql b/test/trace_processor/chrome/touch_jank_test.sql
index d601148..9bab3e7 100644
--- a/test/trace_processor/chrome/touch_jank_test.sql
+++ b/test/trace_processor/chrome/touch_jank_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('chrome/touch_jank.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/touch_jank.sql');
 
 SELECT
   touch_id,
diff --git a/test/trace_processor/graphics/android_jank_cuj_query_test.sql b/test/trace_processor/graphics/android_jank_cuj_query_test.sql
index af5c4ee..831da00 100644
--- a/test/trace_processor/graphics/android_jank_cuj_query_test.sql
+++ b/test/trace_processor/graphics/android_jank_cuj_query_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('android/android_jank_cuj.sql') AS suppress_query_output;
+SELECT RUN_METRIC('android/android_jank_cuj.sql');
 
 
 -- First query to look at `binder transaction` on the Main Thread
diff --git a/test/trace_processor/graphics/android_sysui_cuj_event_test.sql b/test/trace_processor/graphics/android_sysui_cuj_event_test.sql
index e1d1271..7a991a6 100644
--- a/test/trace_processor/graphics/android_sysui_cuj_event_test.sql
+++ b/test/trace_processor/graphics/android_sysui_cuj_event_test.sql
@@ -13,6 +13,6 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('android/android_sysui_cuj.sql') AS suppress_query_output;
+SELECT RUN_METRIC('android/android_sysui_cuj.sql');
 
 SELECT * FROM android_sysui_cuj_event;
\ No newline at end of file
diff --git a/test/trace_processor/graphics/composer_execution_test.sql b/test/trace_processor/graphics/composer_execution_test.sql
index 874e12b..808927c 100644
--- a/test/trace_processor/graphics/composer_execution_test.sql
+++ b/test/trace_processor/graphics/composer_execution_test.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 
 SELECT RUN_METRIC('android/composer_execution.sql',
-  'output', 'hwc_execution_spans') AS suppress_query_output;
+  'output', 'hwc_execution_spans');
 
 SELECT
   validation_type,
diff --git a/test/trace_processor/graphics/composition_layer_count_test.sql b/test/trace_processor/graphics/composition_layer_count_test.sql
index 30a92e6..f4f7dfb 100644
--- a/test/trace_processor/graphics/composition_layer_count_test.sql
+++ b/test/trace_processor/graphics/composition_layer_count_test.sql
@@ -13,7 +13,7 @@
 -- 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 RUN_METRIC('android/android_hwcomposer.sql');
 
 SELECT AVG(value)
 FROM total_layers;
diff --git a/test/trace_processor/graphics/frame_missed_event_test.sql b/test/trace_processor/graphics/frame_missed_event_test.sql
index 4e79c51..c4b275b 100644
--- a/test/trace_processor/graphics/frame_missed_event_test.sql
+++ b/test/trace_processor/graphics/frame_missed_event_test.sql
@@ -13,7 +13,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-SELECT RUN_METRIC('android/android_surfaceflinger.sql') AS suppress_query_output;
+SELECT RUN_METRIC('android/android_surfaceflinger.sql');
 
 SELECT ts, dur
 FROM android_surfaceflinger_event;
\ No newline at end of file
diff --git a/test/trace_processor/smoke/proxy_power_test.sql b/test/trace_processor/smoke/proxy_power_test.sql
index e975273..67f1379 100644
--- a/test/trace_processor/smoke/proxy_power_test.sql
+++ b/test/trace_processor/smoke/proxy_power_test.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('android/android_proxy_power.sql') AS suppress_query_output;
+SELECT RUN_METRIC('android/android_proxy_power.sql');
 
 -- The test trace doesn't contain metadata necessary to determine the device
 -- name, so we create a table with the name directly.
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index 41166fc..aa1454f 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -299,7 +299,6 @@
         result_str += f"Rebasing {expected_path}\n"
         with open(expected_path, 'w') as f:
           f.write(result.actual)
-        rebased += 1
       else:
         result_str += f"Rebase failed for {expected_path} as query failed\n"
 
@@ -335,9 +334,9 @@
       sys.stderr.write(res_str)
       if test_passed:
         perf_data.append(perf_result)
+      else:
         if args.rebase:
           rebased += 1
-      else:
         test_failure.append(test_name)
 
   return test_failure, perf_data, rebased
diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
index 3682c35..16553be 100644
--- a/ui/.eslintrc.js
+++ b/ui/.eslintrc.js
@@ -55,6 +55,13 @@
     'no-array-constructor': 'off',
     '@typescript-eslint/no-array-constructor': ['error'],
 
+    // Rest parameters are not equivalent to 'arguments'.
+    // Rest parameters are arrays: https://developer.mozilla.org/en-US/docs/Web/
+    // JavaScript/Reference/Functions/rest_parameters
+    // 'arguments' are objects: https://developer.mozilla.org/en-US/docs/Web/
+    // JavaScript/Reference/Functions/arguments
+    'prefer-rest-params': 'off',
+
     // We have a lot normal functions which are capitalised.
     // TODO(hjd): Switch these to be lowercase and remove capIsNew.
     // There are also some properties like: foo.factory these should
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 65b82e2..69236ff 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "75b49f42ece55b72377d414fa2d82c12c55cb60b"
+      "rev": "ef7b05231ac5f11ce258c1e14bee6d4b12418aa5"
     },
     {
       "name": "canary",
-      "rev": "5760d032c2c24f15e4eb4ac7b26448b481a73dfa"
+      "rev": "c62a4851b1dafde730c1bf7dc7eb55eaf0b0aa5c"
     },
     {
       "name": "autopush",
diff --git a/ui/src/common/cache_manager.ts b/ui/src/common/cache_manager.ts
index 04bbfde..108dce7 100644
--- a/ui/src/common/cache_manager.ts
+++ b/ui/src/common/cache_manager.ts
@@ -45,7 +45,12 @@
   }
 }
 
-async function getCache(): Promise<Cache> {
+async function getCache(): Promise<Cache|undefined> {
+  if (self.caches === undefined) {
+    // The browser doesn't support cache storage or the page is opened from
+    // a non-secure origin.
+    return undefined;
+  }
   if (LAZY_CACHE !== undefined) {
     return LAZY_CACHE;
   }
@@ -56,6 +61,7 @@
 async function cacheDelete(key: Request): Promise<boolean> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return false;  // Cache storage not supported.
     return cache.delete(key);
   } catch (e) {
     return ignoreNonActionableErrors(e, false);
@@ -65,6 +71,7 @@
 async function cachePut(key: string, value: Response): Promise<void> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return;  // Cache storage not supported.
     cache.put(key, value);
   } catch (e) {
     ignoreNonActionableErrors(e, undefined);
@@ -74,6 +81,7 @@
 async function cacheMatch(key: Request|string): Promise<Response|undefined> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return undefined;  // Cache storage not supported.
     return cache.match(key);
   } catch (e) {
     return ignoreNonActionableErrors(e, undefined);
@@ -83,6 +91,7 @@
 async function cacheKeys(): Promise<readonly Request[]> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return [];  // Cache storage not supported.
     return cache.keys();
   } catch (e) {
     return ignoreNonActionableErrors(e, []);
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index fc5c771..ec79094 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -85,6 +85,7 @@
 const UFS_CMD_TAG = 'io.ufs.command.tag';
 const UFS_CMD_TAG_GROUP_NAME = 'io.ufs.command.tags';
 const BUDDY_INFO_TAG = 'mem.buddyinfo';
+const KERNEL_WAKELOCK_PREFIX = 'Wakelock';
 
 // Sets the default 'scale' for counter tracks. If the regex matches
 // then the paired mode is used. Entries are in priority order so the
@@ -608,6 +609,42 @@
     }
   }
 
+  async groupKernelWakelockTracks(): Promise<void> {
+    let groupUuid = undefined;
+
+    for (const track of this.tracksToAdd) {
+      // NB: Userspace wakelocks start with "WakeLock" not "Wakelock".
+      if (track.name.startsWith(KERNEL_WAKELOCK_PREFIX)) {
+        if (groupUuid === undefined) {
+          groupUuid = uuidv4();
+        }
+        track.trackGroup = groupUuid;
+      }
+    }
+
+    if (groupUuid !== undefined) {
+      const summaryTrackId = uuidv4();
+      this.tracksToAdd.push({
+        id: summaryTrackId,
+        engineId: this.engineId,
+        kind: NULL_TRACK_KIND,
+        trackSortKey: PrimaryTrackSortKey.NULL_TRACK,
+        name: 'Kernel wakelocks',
+        trackGroup: undefined,
+        config: {},
+      });
+
+      const addGroup = Actions.addTrackGroup({
+        engineId: this.engineId,
+        summaryTrackId,
+        name: 'Kernel wakelocks',
+        id: groupUuid,
+        collapsed: true,
+      });
+      this.addTrackGroupActions.push(addGroup);
+    }
+  }
+
   async addLogsTrack(): Promise<void> {
     const result =
         await this.engine.query(`select count(1) as cnt from android_logs`);
@@ -1587,6 +1624,7 @@
     await this.groupGlobalUfsCmdTagTracks(
         UFS_CMD_TAG, UFS_CMD_TAG_GROUP_NAME);
     await this.groupGlobalBuddyInfoTracks();
+    await this.groupKernelWakelockTracks();
 
     // Pre-group all kernel "threads" (actually processes) if this is a linux
     // system trace. Below, addProcessTrackGroups will skip them due to an
diff --git a/ui/src/frontend/analytics.ts b/ui/src/frontend/analytics.ts
index cdb9d12..87f5456 100644
--- a/ui/src/frontend/analytics.ts
+++ b/ui/src/frontend/analytics.ts
@@ -69,10 +69,10 @@
     // [1] https://developers.google.com/analytics/devguides/collection/gtagjs .
     gtagGlobals.dataLayer = gtagGlobals.dataLayer || [];
 
-    function gtagFunction(...args: any[]) {
+    function gtagFunction(..._: any[]) {
       // This needs to be a function and not a lambda. |arguments| behaves
       // slightly differently in a lambda and breaks GA.
-      gtagGlobals.dataLayer.push(args);
+      gtagGlobals.dataLayer.push(arguments);
     }
     gtagGlobals.gtag = gtagFunction;
     gtagGlobals.gtag('js', new Date());