Merge "[stdlib]: Rewrite the critical path implementation" into main
diff --git a/Android.bp b/Android.bp
index 724d479..79d28ce 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15166,70 +15166,38 @@
         ":perfetto_include_perfetto_trace_processor_basic_types",
         ":perfetto_include_perfetto_trace_processor_storage",
         ":perfetto_include_perfetto_trace_processor_trace_processor",
-        ":perfetto_protos_perfetto_common_cpp_gen",
         ":perfetto_protos_perfetto_common_zero_gen",
-        ":perfetto_protos_perfetto_config_android_cpp_gen",
         ":perfetto_protos_perfetto_config_android_zero_gen",
-        ":perfetto_protos_perfetto_config_cpp_gen",
-        ":perfetto_protos_perfetto_config_ftrace_cpp_gen",
         ":perfetto_protos_perfetto_config_ftrace_zero_gen",
-        ":perfetto_protos_perfetto_config_gpu_cpp_gen",
         ":perfetto_protos_perfetto_config_gpu_zero_gen",
-        ":perfetto_protos_perfetto_config_inode_file_cpp_gen",
         ":perfetto_protos_perfetto_config_inode_file_zero_gen",
-        ":perfetto_protos_perfetto_config_interceptors_cpp_gen",
         ":perfetto_protos_perfetto_config_interceptors_zero_gen",
-        ":perfetto_protos_perfetto_config_power_cpp_gen",
         ":perfetto_protos_perfetto_config_power_zero_gen",
-        ":perfetto_protos_perfetto_config_process_stats_cpp_gen",
         ":perfetto_protos_perfetto_config_process_stats_zero_gen",
-        ":perfetto_protos_perfetto_config_profiling_cpp_gen",
         ":perfetto_protos_perfetto_config_profiling_zero_gen",
-        ":perfetto_protos_perfetto_config_statsd_cpp_gen",
         ":perfetto_protos_perfetto_config_statsd_zero_gen",
-        ":perfetto_protos_perfetto_config_sys_stats_cpp_gen",
         ":perfetto_protos_perfetto_config_sys_stats_zero_gen",
-        ":perfetto_protos_perfetto_config_system_info_cpp_gen",
         ":perfetto_protos_perfetto_config_system_info_zero_gen",
-        ":perfetto_protos_perfetto_config_track_event_cpp_gen",
         ":perfetto_protos_perfetto_config_track_event_zero_gen",
         ":perfetto_protos_perfetto_config_zero_gen",
-        ":perfetto_protos_perfetto_trace_android_cpp_gen",
         ":perfetto_protos_perfetto_trace_android_zero_gen",
-        ":perfetto_protos_perfetto_trace_chrome_cpp_gen",
         ":perfetto_protos_perfetto_trace_chrome_zero_gen",
-        ":perfetto_protos_perfetto_trace_etw_cpp_gen",
         ":perfetto_protos_perfetto_trace_etw_zero_gen",
-        ":perfetto_protos_perfetto_trace_filesystem_cpp_gen",
         ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
-        ":perfetto_protos_perfetto_trace_ftrace_cpp_gen",
         ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
-        ":perfetto_protos_perfetto_trace_gpu_cpp_gen",
         ":perfetto_protos_perfetto_trace_gpu_zero_gen",
-        ":perfetto_protos_perfetto_trace_interned_data_cpp_gen",
         ":perfetto_protos_perfetto_trace_interned_data_zero_gen",
-        ":perfetto_protos_perfetto_trace_minimal_cpp_gen",
         ":perfetto_protos_perfetto_trace_minimal_zero_gen",
-        ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen",
         ":perfetto_protos_perfetto_trace_non_minimal_zero_gen",
-        ":perfetto_protos_perfetto_trace_perfetto_cpp_gen",
         ":perfetto_protos_perfetto_trace_perfetto_zero_gen",
-        ":perfetto_protos_perfetto_trace_power_cpp_gen",
         ":perfetto_protos_perfetto_trace_power_zero_gen",
         ":perfetto_protos_perfetto_trace_processor_zero_gen",
-        ":perfetto_protos_perfetto_trace_profiling_cpp_gen",
         ":perfetto_protos_perfetto_trace_profiling_zero_gen",
-        ":perfetto_protos_perfetto_trace_ps_cpp_gen",
         ":perfetto_protos_perfetto_trace_ps_zero_gen",
-        ":perfetto_protos_perfetto_trace_statsd_cpp_gen",
         ":perfetto_protos_perfetto_trace_statsd_zero_gen",
-        ":perfetto_protos_perfetto_trace_sys_stats_cpp_gen",
         ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
-        ":perfetto_protos_perfetto_trace_system_info_cpp_gen",
         ":perfetto_protos_perfetto_trace_system_info_zero_gen",
-        ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
         ":perfetto_protos_perfetto_trace_track_event_zero_gen",
-        ":perfetto_protos_perfetto_trace_translation_cpp_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
         ":perfetto_src_base_base",
         ":perfetto_src_protozero_protozero",
@@ -15272,70 +15240,38 @@
         "libz",
     ],
     generated_headers: [
-        "perfetto_protos_perfetto_common_cpp_gen_headers",
         "perfetto_protos_perfetto_common_zero_gen_headers",
-        "perfetto_protos_perfetto_config_android_cpp_gen_headers",
         "perfetto_protos_perfetto_config_android_zero_gen_headers",
-        "perfetto_protos_perfetto_config_cpp_gen_headers",
-        "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
         "perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
-        "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
         "perfetto_protos_perfetto_config_gpu_zero_gen_headers",
-        "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
         "perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
-        "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers",
         "perfetto_protos_perfetto_config_interceptors_zero_gen_headers",
-        "perfetto_protos_perfetto_config_power_cpp_gen_headers",
         "perfetto_protos_perfetto_config_power_zero_gen_headers",
-        "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
         "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
-        "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
         "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
-        "perfetto_protos_perfetto_config_statsd_cpp_gen_headers",
         "perfetto_protos_perfetto_config_statsd_zero_gen_headers",
-        "perfetto_protos_perfetto_config_sys_stats_cpp_gen_headers",
         "perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
-        "perfetto_protos_perfetto_config_system_info_cpp_gen_headers",
         "perfetto_protos_perfetto_config_system_info_zero_gen_headers",
-        "perfetto_protos_perfetto_config_track_event_cpp_gen_headers",
         "perfetto_protos_perfetto_config_track_event_zero_gen_headers",
         "perfetto_protos_perfetto_config_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_android_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_etw_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_etw_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_power_zero_gen_headers",
         "perfetto_protos_perfetto_trace_processor_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_statsd_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_statsd_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_sys_stats_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_system_info_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
-        "perfetto_protos_perfetto_trace_translation_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
         "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
         "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index fe67ac8..3630898 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -61,9 +61,7 @@
     "../../include/perfetto/ext/base",
     "../../include/perfetto/protozero:protozero",
     "../../include/perfetto/trace_processor:storage",
-    "../../protos/perfetto/trace:non_minimal_cpp",
     "../../protos/perfetto/trace:non_minimal_zero",
-    "../../protos/perfetto/trace/android:cpp",
     "../../protos/perfetto/trace/android:zero",
     "../../protos/perfetto/trace/ftrace:zero",
     "../../protos/perfetto/trace/ps:zero",
@@ -76,6 +74,7 @@
   sources = [
     "scrub_ftrace_events_integrationtest.cc",
     "scrub_process_trees_integrationtest.cc",
+    "scrub_task_rename_integrationtest.cc",
     "trace_redactor_integrationtest.cc",
   ]
   deps = [
diff --git a/src/trace_redaction/prune_package_list.cc b/src/trace_redaction/prune_package_list.cc
index a85a18f..de6331b 100644
--- a/src/trace_redaction/prune_package_list.cc
+++ b/src/trace_redaction/prune_package_list.cc
@@ -18,15 +18,29 @@
 
 #include <string>
 
+#include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
 
-#include "protos/perfetto/trace/android/packages_list.gen.h"
-#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/android/packages_list.pbzero.h"
+#include "src/trace_redaction/proto_util.h"
 
 namespace perfetto::trace_redaction {
+namespace {
 
-PrunePackageList::PrunePackageList() = default;
-PrunePackageList::~PrunePackageList() = default;
+bool ShouldKeepPackageInfo(protozero::Field package_info, uint64_t uid) {
+  PERFETTO_DCHECK(package_info.id() ==
+                  protos::pbzero::PackagesList::kPackagesFieldNumber);
+
+  protozero::ProtoDecoder decoder(package_info.as_bytes());
+  auto uid_field = decoder.FindField(
+      protos::pbzero::PackagesList::PackageInfo::kUidFieldNumber);
+
+  return uid_field.valid() &&
+         NormalizeUid(uid_field.as_uint64()) == NormalizeUid(uid);
+}
+
+}  // namespace
 
 base::Status PrunePackageList::Transform(const Context& context,
                                          std::string* packet) const {
@@ -34,30 +48,47 @@
     return base::ErrStatus("PrunePackageList: missing package uid.");
   }
 
+  protozero::ProtoDecoder packet_decoder(*packet);
+
   protos::pbzero::TracePacket::Decoder trace_packet_decoder(*packet);
 
-  if (!trace_packet_decoder.has_packages_list()) {
+  auto package_list = packet_decoder.FindField(
+      protos::pbzero::TracePacket::kPackagesListFieldNumber);
+
+  if (!package_list.valid()) {
     return base::OkStatus();
   }
 
-  auto normalized_uid = NormalizeUid(context.package_uid.value());
+  auto uid = context.package_uid.value();
 
-  protos::gen::TracePacket mutable_packet;
-  mutable_packet.ParseFromString(*packet);
+  protozero::HeapBuffered<protos::pbzero::TracePacket> packet_message;
 
-  auto* packages = mutable_packet.mutable_packages_list()->mutable_packages();
+  for (auto packet_field = packet_decoder.ReadField(); packet_field.valid();
+       packet_field = packet_decoder.ReadField()) {
+    if (packet_field.id() !=
+        protos::pbzero::TracePacket::kPackagesListFieldNumber) {
+      proto_util::AppendField(packet_field, packet_message.get());
+      continue;
+    }
 
-  // Remove all entries that don't match the uid. After this, one or more
-  // packages will be left in the list (multiple packages can share a uid).
-  packages->erase(
-      std::remove_if(
-          packages->begin(), packages->end(),
-          [normalized_uid](const protos::gen::PackagesList::PackageInfo& info) {
-            return NormalizeUid(info.uid()) != normalized_uid;
-          }),
-      packages->end());
+    auto* package_list_message = packet_message->set_packages_list();
 
-  packet->assign(mutable_packet.SerializeAsString());
+    protozero::ProtoDecoder package_list_decoder(packet_field.as_bytes());
+
+    for (auto package_field = package_list_decoder.ReadField();
+         package_field.valid();
+         package_field = package_list_decoder.ReadField()) {
+      // If not packages, keep.
+      // If packages and uid matches, keep.
+      if (package_field.id() !=
+              protos::pbzero::PackagesList::kPackagesFieldNumber ||
+          ShouldKeepPackageInfo(package_field, uid)) {
+        proto_util::AppendField(package_field, package_list_message);
+      }
+    }
+  }
+
+  packet->assign(packet_message.SerializeAsString());
 
   return base::OkStatus();
 }
diff --git a/src/trace_redaction/prune_package_list.h b/src/trace_redaction/prune_package_list.h
index 24d9ec2..ff5f060 100644
--- a/src/trace_redaction/prune_package_list.h
+++ b/src/trace_redaction/prune_package_list.h
@@ -28,9 +28,6 @@
 // Returns `base::ErrStatus()` if `Context.package_uid` was not set.
 class PrunePackageList final : public TransformPrimitive {
  public:
-  PrunePackageList();
-  ~PrunePackageList() override;
-
   base::Status Transform(const Context& context,
                          std::string* packet) const override;
 };
diff --git a/ui/src/base/logging.ts b/ui/src/base/logging.ts
index 10fbbff..74cbe44 100644
--- a/ui/src/base/logging.ts
+++ b/ui/src/base/logging.ts
@@ -60,7 +60,7 @@
 
   if (err instanceof ErrorEvent) {
     errType = 'ERROR';
-    errMsg = err.message;
+    errMsg = `${err.error}`;
     errorObj = err.error;
   } else if (err instanceof PromiseRejectionEvent) {
     errType = 'PROMISE_REJ';
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 42dac7a..4f16d80 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -80,6 +80,7 @@
   trackSortKey: TrackSortKey;
   trackGroup?: string;
   params?: unknown;
+  closeable?: boolean;
 }
 
 export interface PostedTrace {
@@ -211,6 +212,7 @@
         labels: track.labels,
         uri: track.uri,
         params: track.params,
+        closeable: track.closeable,
       };
       if (track.trackGroup === SCROLLING_TRACK_GROUP) {
         state.scrollingTracks.push(trackKey);
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index bce3b4f..92efee5 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -281,6 +281,7 @@
   trackGroup?: string;
   params?: unknown;
   state?: unknown;
+  closeable?: boolean;
 }
 
 export interface TrackGroupState {
diff --git a/ui/src/frontend/debug_tracks.ts b/ui/src/frontend/debug_tracks.ts
index 7e2ce7e..6ff6297 100644
--- a/ui/src/frontend/debug_tracks.ts
+++ b/ui/src/frontend/debug_tracks.ts
@@ -67,7 +67,6 @@
   const trackConfig: DebugTrackV2Config = {
     data,
     columns: sliceColumns,
-    closeable,
     argColumns,
   };
 
@@ -79,6 +78,7 @@
       trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
       trackGroup: SCROLLING_TRACK_GROUP,
       params: trackConfig,
+      closeable,
     }),
   ];
   if (config?.pinned ?? true) {
@@ -117,7 +117,6 @@
 export interface CounterDebugTrackConfig {
   data: SqlDataSource;
   columns: CounterColumns;
-  closeable: boolean;
 }
 
 export interface CounterDebugTrackCreateConfig {
@@ -143,7 +142,6 @@
   const params: CounterDebugTrackConfig = {
     data,
     columns,
-    closeable,
   };
 
   const trackKey = uuidv4();
@@ -155,6 +153,7 @@
       trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
       trackGroup: SCROLLING_TRACK_GROUP,
       params,
+      closeable,
     }),
   ];
   if (config?.pinned ?? true) {
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 2e15d67..4240ef4 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -37,6 +37,7 @@
 import {canvasClip} from '../common/canvas_utils';
 import {TimeScale} from './time_scale';
 import {getLegacySelection} from '../common/state';
+import {CloseTrackButton} from './close_track_button';
 
 function getTitleSize(title: string): string | undefined {
   const length = title.length;
@@ -330,6 +331,7 @@
   tags?: TrackTags;
   track?: Track;
   error?: Error | undefined;
+  closeable: boolean;
 
   // Issues a scrollTo() on this DOM element at creation time. Default: false.
   revealOnCreate?: boolean;
@@ -359,6 +361,7 @@
         m(TrackShell, {
           buttons: [
             attrs.error && m(CrashButton, {error: attrs.error}),
+            attrs.closeable && m(CloseTrackButton, {trackKey: attrs.trackKey}),
             attrs.buttons,
           ],
           title: attrs.title,
@@ -426,6 +429,7 @@
   tags?: TrackTags;
   trackFSM?: TrackCacheEntry;
   revealOnCreate?: boolean;
+  closeable: boolean;
 }
 
 export class TrackPanel implements Panel {
@@ -452,6 +456,7 @@
           trackKey: attrs.trackKey,
           error: attrs.trackFSM.getError(),
           track: attrs.trackFSM.track,
+          closeable: attrs.closeable,
         });
       }
       return m(TrackComponent, {
@@ -463,12 +468,14 @@
         track: attrs.trackFSM.track,
         error: attrs.trackFSM.getError(),
         revealOnCreate: attrs.revealOnCreate,
+        closeable: attrs.closeable,
       });
     } else {
       return m(TrackComponent, {
         trackKey: attrs.trackKey,
         title: attrs.title,
         revealOnCreate: attrs.revealOnCreate,
+        closeable: attrs.closeable,
       });
     }
   }
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 3f12a7d..719705e 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -241,6 +241,7 @@
           title: trackBundle.title,
           tags: trackBundle.tags,
           trackFSM: trackBundle.trackFSM,
+          closeable: trackBundle.closeable,
         });
       },
     );
@@ -270,6 +271,7 @@
             title: trackBundle.title,
             tags: trackBundle.tags,
             trackFSM: trackBundle.trackFSM,
+            closeable: trackBundle.closeable,
           });
           childTracks.push(panel);
         }
@@ -327,6 +329,7 @@
               tags: trackBundle.tags,
               trackFSM: trackBundle.trackFSM,
               revealOnCreate: true,
+              closeable: trackBundle.closeable,
             });
           }),
           kind: 'TRACKS',
@@ -348,7 +351,7 @@
   // Resolve a track and its metadata through the track cache
   private resolveTrack(key: string): TrackBundle {
     const trackState = globals.state.tracks[key];
-    const {uri, params, name, labels} = trackState;
+    const {uri, params, name, labels, closeable} = trackState;
     const trackDesc = globals.trackManager.resolveTrackInfo(uri);
     const trackCacheEntry =
       trackDesc && globals.trackManager.resolveTrack(key, trackDesc, params);
@@ -361,6 +364,7 @@
       trackFSM,
       labels,
       trackIds,
+      closeable: closeable ?? false,
     };
   }
 
@@ -371,6 +375,7 @@
 
 interface TrackBundle {
   title: string;
+  closeable: boolean;
   trackFSM?: TrackCacheEntry;
   tags?: TrackTags;
   labels?: string[];
diff --git a/ui/src/plugins/dev.perfetto.Chaos/OWNERS b/ui/src/plugins/dev.perfetto.Chaos/OWNERS
new file mode 100644
index 0000000..9ee9fce
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.Chaos/OWNERS
@@ -0,0 +1 @@
+hjd@google.com
diff --git a/ui/src/plugins/dev.perfetto.Chaos/index.ts b/ui/src/plugins/dev.perfetto.Chaos/index.ts
new file mode 100644
index 0000000..c18ff7f
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.Chaos/index.ts
@@ -0,0 +1,81 @@
+// Copyright (C) 2024 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 {
+  Plugin,
+  PluginContext,
+  PluginContextTrace,
+  PluginDescriptor,
+  addDebugSliceTrack,
+} from '../../public';
+
+class Chaos implements Plugin {
+  onActivate(ctx: PluginContext): void {
+    ctx.registerCommand({
+      id: 'dev.perfetto.Chaos#CrashNow',
+      name: 'Chaos: crash now',
+      callback: () => {
+        throw new Error('Manual crash from dev.perfetto.Chaos#CrashNow');
+      },
+    });
+  }
+
+  async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+    ctx.registerCommand({
+      id: 'dev.perfetto.Chaos#CrashNowQuery',
+      name: 'Chaos: run crashing query',
+      callback: () => {
+        ctx.engine.query(`this is a
+          syntactically
+          invalid
+          query
+          over
+          many
+          lines
+        `);
+      },
+    });
+
+    ctx.registerCommand({
+      id: 'dev.perfetto.Chaos#AddCrashingDebugTrack',
+      name: 'Chaos: add crashing debug track',
+      callback: () => {
+        addDebugSliceTrack(
+          ctx.engine,
+          {
+            sqlSource: `
+            syntactically
+            invalid
+            query
+            over
+            many
+          `,
+          },
+          `Chaos track`,
+          {ts: 'ts', dur: 'dur', name: 'name'},
+          [],
+        );
+      },
+    });
+  }
+
+  async onTraceUnload(_: PluginContextTrace): Promise<void> {}
+
+  onDeactivate(_: PluginContext): void {}
+}
+
+export const plugin: PluginDescriptor = {
+  pluginId: 'dev.perfetto.Chaos',
+  plugin: Chaos,
+};
diff --git a/ui/src/tracks/debug/counter_track.ts b/ui/src/tracks/debug/counter_track.ts
index d61d573..5b37e3d 100644
--- a/ui/src/tracks/debug/counter_track.ts
+++ b/ui/src/tracks/debug/counter_track.ts
@@ -14,10 +14,7 @@
 
 import m from 'mithril';
 
-import {Actions} from '../../common/actions';
 import {BaseCounterTrack} from '../../frontend/base_counter_track';
-import {globals} from '../../frontend/globals';
-import {TrackButton} from '../../frontend/track_panel';
 import {TrackContext} from '../../public';
 import {EngineProxy} from '../../trace_processor/engine';
 import {CounterDebugTrackConfig} from '../../frontend/debug_tracks';
@@ -49,20 +46,7 @@
   }
 
   getTrackShellButtons(): m.Children {
-    return [
-      this.getCounterContextMenu(),
-      this.config.closeable &&
-        m(TrackButton, {
-          action: () => {
-            globals.dispatch(
-              Actions.removeTracks({trackKeys: [this.trackKey]}),
-            );
-          },
-          i: 'close',
-          tooltip: 'Close',
-          showButton: true,
-        }),
-    ];
+    return this.getCounterContextMenu();
   }
 
   getSqlSource(): string {
diff --git a/ui/src/tracks/debug/slice_track.ts b/ui/src/tracks/debug/slice_track.ts
index dbab2be..4ea16dc 100644
--- a/ui/src/tracks/debug/slice_track.ts
+++ b/ui/src/tracks/debug/slice_track.ts
@@ -12,12 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import m from 'mithril';
-
-import {Actions} from '../../common/actions';
-import {globals} from '../../frontend/globals';
 import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {TrackButton} from '../../frontend/track_panel';
 import {TrackContext} from '../../public';
 import {EngineProxy} from '../../trace_processor/engine';
 import {
@@ -38,7 +33,6 @@
 export interface DebugTrackV2Config {
   data: SqlDataSource;
   columns: SliceColumns;
-  closeable: boolean;
   argColumns: string[];
 }
 
@@ -81,21 +75,6 @@
     };
   }
 
-  getTrackShellButtons(): m.Children {
-    return this.config.closeable
-      ? m(TrackButton, {
-          action: () => {
-            globals.dispatch(
-              Actions.removeTracks({trackKeys: [this.trackKey]}),
-            );
-          },
-          i: 'close',
-          tooltip: 'Close',
-          showButton: true,
-        })
-      : [];
-  }
-
   private async createTrackTable(
     data: SqlDataSource,
     sliceColumns: SliceColumns,