• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {BigintMath} from '../base/bigint_math';
16import {duration, time} from '../base/time';
17import {RecordConfig} from '../controller/record_config_types';
18import {
19  Aggregation,
20  PivotTree,
21  TableColumn,
22} from '../frontend/pivot_table_types';
23
24import {
25  selectionToLegacySelection,
26  Selection,
27  LegacySelection,
28} from '../core/selection_manager';
29
30export {
31  Selection,
32  SelectionKind,
33  NoteSelection,
34  SliceSelection,
35  HeapProfileSelection,
36  PerfSamplesSelection,
37  LegacySelection,
38  AreaSelection,
39  ProfileType,
40  ThreadSliceSelection,
41  CpuProfileSampleSelection,
42} from '../core/selection_manager';
43
44// Tracks within track groups (usually corresponding to processes) are sorted.
45// As we want to group all tracks related to a given thread together, we use
46// two keys:
47// - Primary key corresponds to a priority of a track block (all tracks related
48//   to a given thread or a single track if it's not thread-associated).
49// - Secondary key corresponds to a priority of a given thread-associated track
50//   within its thread track block.
51// Each track will have a sort key, which either a primary sort key
52// (for non-thread tracks) or a tid and secondary sort key (mapping of tid to
53// primary sort key is done independently).
54export enum PrimaryTrackSortKey {
55  DEBUG_TRACK,
56  NULL_TRACK,
57  PROCESS_SCHEDULING_TRACK,
58  PROCESS_SUMMARY_TRACK,
59  EXPECTED_FRAMES_SLICE_TRACK,
60  ACTUAL_FRAMES_SLICE_TRACK,
61  PERF_SAMPLES_PROFILE_TRACK,
62  HEAP_PROFILE_TRACK,
63  MAIN_THREAD,
64  RENDER_THREAD,
65  GPU_COMPLETION_THREAD,
66  CHROME_IO_THREAD,
67  CHROME_COMPOSITOR_THREAD,
68  ORDINARY_THREAD,
69  COUNTER_TRACK,
70  ASYNC_SLICE_TRACK,
71  ORDINARY_TRACK,
72}
73
74/**
75 * A plain js object, holding objects of type |Class| keyed by string id.
76 * We use this instead of using |Map| object since it is simpler and faster to
77 * serialize for use in postMessage.
78 */
79export interface ObjectById<Class extends {id: string}> {
80  [id: string]: Class;
81}
82
83// Same as ObjectById but the key parameter is called `key` rather than `id`.
84export interface ObjectByKey<Class extends {key: string}> {
85  [key: string]: Class;
86}
87
88export interface Timestamped {
89  lastUpdate: number;
90}
91
92export type OmniboxMode = 'SEARCH' | 'COMMAND';
93
94export interface OmniboxState {
95  omnibox: string;
96  mode: OmniboxMode;
97  force?: boolean;
98}
99
100// This is simply an arbitrarily large number to default to.
101export const RESOLUTION_DEFAULT = BigintMath.bitFloor(1_000_000_000_000n);
102
103export interface VisibleState extends Timestamped {
104  start: time;
105  end: time;
106  resolution: duration;
107}
108
109export interface Area {
110  start: time;
111  end: time;
112  tracks: string[];
113}
114
115export const MAX_TIME = 180;
116
117// 3: TrackKindPriority and related sorting changes.
118// 5: Move a large number of items off frontendLocalState and onto state.
119// 6: Common PivotTableConfig and pivot table specific PivotTableState.
120// 7: Split Chrome categories in two and add 'symbolize ksyms' flag.
121// 8: Rename several variables
122// "[...]HeapProfileFlamegraph[...]" -> "[...]Flamegraph[...]".
123// 9: Add a field to track last loaded recording profile name
124// 10: Change last loaded profile tracking type to accommodate auto-save.
125// 11: Rename updateChromeCategories to fetchChromeCategories.
126// 12: Add a field to cache mapping from UI track ID to trace track ID in order
127//     to speed up flow arrows rendering.
128// 13: FlamegraphState changed to support area selection.
129// 14: Changed the type of uiTrackIdByTraceTrackId from `Map` to an object with
130// typed key/value because a `Map` does not preserve type during
131// serialisation+deserialisation.
132// 15: Added state for Pivot Table V2
133// 16: Added boolean tracking if the flamegraph modal was dismissed
134// 17:
135// - add currentEngineId to track the id of the current engine
136// - remove nextNoteId, nextAreaId and use nextId as a unique counter for all
137//   indexing except the indexing of the engines
138// 18: areaSelection change see b/235869542
139// 19: Added visualisedArgs state.
140// 20: Refactored thread sorting order.
141// 21: Updated perf sample selection to include a ts range instead of single ts
142// 22: Add log selection kind.
143// 23: Add log filtering criteria for Android log entries.
144// 24: Store only a single Engine.
145// 25: Move omnibox state off VisibleState.
146// 26: Add tags for filtering Android log entries.
147// 27. Add a text entry for filtering Android log entries.
148// 28. Add a boolean indicating if non matching log entries are hidden.
149// 29. Add ftrace state. <-- Borked, state contains a non-serializable object.
150// 30. Convert ftraceFilter.excludedNames from Set<string> to string[].
151// 31. Convert all timestamps to bigints.
152// 32. Add pendingDeeplink.
153// 33. Add plugins state.
154// 34. Add additional pendingDeeplink fields (query, pid).
155// 35. Add force to OmniboxState
156// 36. Remove metrics
157// 37. Add additional pendingDeeplink fields (visStart, visEnd).
158// 38. Add track tags.
159// 39. Ported cpu_slice, ftrace, and android_log tracks to plugin tracks. Track
160//     state entries now require a URI and old track implementations are no
161//     longer registered.
162// 40. Ported counter, process summary/sched, & cpu_freq to plugin tracks.
163// 41. Ported all remaining tracks.
164// 42. Rename trackId -> trackKey.
165// 43. Remove visibleTracks.
166// 44. Add TabsV2 state.
167// 45. Remove v1 tracks.
168// 46. Remove trackKeyByTrackId.
169// 47. Selection V2
170// 48. Rename legacySelection -> selection and introduce new Selection type.
171// 49. Remove currentTab, which is only relevant to TabsV1.
172// 50. Remove ftrace filter state.
173// 51. Changed structure of FlamegraphState.expandedCallsiteByViewingOption.
174// 52. Update track group state - don't make the summary track the first track.
175// 53. Remove android log state.
176// 54. Remove traceTime.
177// 55. Rename TrackGroupState.id -> TrackGroupState.key.
178// 56. Renamed chrome slice to thread slice everywhere.
179// 57. Remove flamegraph related code from state.
180// 58. Remove area map.
181// 59. Deprecate old area selection type.
182// 60. Deprecate old note selection type.
183// 61. Remove params/state from TrackState.
184export const STATE_VERSION = 61;
185
186export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
187
188export type EngineMode = 'WASM' | 'HTTP_RPC';
189
190export type NewEngineMode = 'USE_HTTP_RPC_IF_AVAILABLE' | 'FORCE_BUILTIN_WASM';
191
192// Key that is used to sort tracks within a block of tracks associated with a
193// given thread.
194export enum InThreadTrackSortKey {
195  THREAD_COUNTER_TRACK,
196  THREAD_SCHEDULING_STATE_TRACK,
197  CPU_STACK_SAMPLES_TRACK,
198  VISUALISED_ARGS_TRACK,
199  ORDINARY,
200  DEFAULT_TRACK,
201}
202
203// Sort key used for sorting tracks associated with a thread.
204export type ThreadTrackSortKey = {
205  utid: number;
206  priority: InThreadTrackSortKey;
207};
208
209// Sort key for all tracks: both thread-associated and non-thread associated.
210export type TrackSortKey = PrimaryTrackSortKey | ThreadTrackSortKey;
211
212// Mapping which defines order for threads within a given process.
213export type UtidToTrackSortKey = {
214  [utid: number]: {
215    tid?: number;
216    sortKey: PrimaryTrackSortKey;
217  };
218};
219
220export interface TraceFileSource {
221  type: 'FILE';
222  file: File;
223}
224
225export interface TraceArrayBufferSource {
226  type: 'ARRAY_BUFFER';
227  buffer: ArrayBuffer;
228  title: string;
229  url?: string;
230  fileName?: string;
231
232  // |uuid| is set only when loading via ?local_cache_key=1234. When set,
233  // this matches global.state.traceUuid, with the exception of the following
234  // time window: When a trace T1 is loaded and the user loads another trace T2,
235  // this |uuid| will be == T2, but the globals.state.traceUuid will be
236  // temporarily == T1 until T2 has been loaded (consistently to what happens
237  // with all other state fields).
238  uuid?: string;
239  // if |localOnly| is true then the trace should not be shared or downloaded.
240  localOnly?: boolean;
241
242  // The set of extra args, keyed by plugin, that can be passed when opening the
243  // trace via postMessge deep-linking. See post_message_handler.ts for details.
244  pluginArgs?: {[pluginId: string]: {[key: string]: unknown}};
245}
246
247export interface TraceUrlSource {
248  type: 'URL';
249  url: string;
250}
251
252export interface TraceHttpRpcSource {
253  type: 'HTTP_RPC';
254}
255
256export type TraceSource =
257  | TraceFileSource
258  | TraceArrayBufferSource
259  | TraceUrlSource
260  | TraceHttpRpcSource;
261
262export interface TrackState {
263  uri: string;
264  key: string;
265  name: string;
266  labels?: string[];
267  trackSortKey: TrackSortKey;
268  trackGroup?: string;
269  closeable?: boolean;
270}
271
272export interface TrackGroupState {
273  key: string;
274  name: string;
275  collapsed: boolean;
276  tracks: string[]; // Child track ids.
277  fixedOrdering?: boolean; // Render tracks without sorting.
278  summaryTrack: string | undefined;
279}
280
281export interface EngineConfig {
282  id: string;
283  mode?: EngineMode; // Is undefined until |ready| is true.
284  ready: boolean;
285  failed?: string; // If defined the engine has crashed with the given message.
286  source: TraceSource;
287}
288
289export interface QueryConfig {
290  id: string;
291  engineId?: string;
292  query: string;
293}
294
295export interface FrontendLocalState {
296  visibleState: VisibleState;
297}
298
299export interface Status {
300  msg: string;
301  timestamp: number; // Epoch in seconds (Date.now() / 1000).
302}
303
304export interface Note {
305  noteType: 'DEFAULT';
306  id: string;
307  timestamp: time;
308  color: string;
309  text: string;
310}
311
312export interface SpanNote {
313  noteType: 'SPAN';
314  id: string;
315  start: time;
316  end: time;
317  color: string;
318  text: string;
319}
320
321export interface Pagination {
322  offset: number;
323  count: number;
324}
325
326export interface RecordingTarget {
327  name: string;
328  os: TargetOs;
329}
330
331export interface AdbRecordingTarget extends RecordingTarget {
332  serial: string;
333}
334
335export interface Sorting {
336  column: string;
337  direction: 'DESC' | 'ASC';
338}
339
340export interface AggregationState {
341  id: string;
342  sorting?: Sorting;
343}
344
345// Auxiliary metadata needed to parse the query result, as well as to render it
346// correctly. Generated together with the text of query and passed without the
347// change to the query response.
348export interface PivotTableQueryMetadata {
349  pivotColumns: TableColumn[];
350  aggregationColumns: Aggregation[];
351  countIndex: number;
352}
353
354// Everything that's necessary to run the query for pivot table
355export interface PivotTableQuery {
356  text: string;
357  metadata: PivotTableQueryMetadata;
358}
359
360// Pivot table query result
361export interface PivotTableResult {
362  // Hierarchical pivot structure on top of rows
363  tree: PivotTree;
364  // Copy of the query metadata from the request, bundled up with the query
365  // result to ensure the correct rendering.
366  metadata: PivotTableQueryMetadata;
367}
368
369// Input parameters to check whether the pivot table needs to be re-queried.
370export interface PivotTableAreaState {
371  start: time;
372  end: time;
373  tracks: string[];
374}
375
376export interface PivotTableState {
377  // Currently selected area, if null, pivot table is not going to be visible.
378  selectionArea?: PivotTableAreaState;
379
380  // Query response
381  queryResult: PivotTableResult | null;
382
383  // Selected pivots for tables other than slice.
384  // Because of the query generation, pivoting happens first on non-slice
385  // pivots; therefore, those can't be put after slice pivots. In order to
386  // maintain the separation more clearly, slice and non-slice pivots are
387  // located in separate arrays.
388  selectedPivots: TableColumn[];
389
390  // Selected aggregation columns. Stored same way as pivots.
391  selectedAggregations: Aggregation[];
392
393  // Whether the pivot table results should be constrained to the selected area.
394  constrainToArea: boolean;
395
396  // Set to true by frontend to request controller to perform the query to
397  // acquire the necessary data from the engine.
398  queryRequested: boolean;
399}
400
401export interface LoadedConfigNone {
402  type: 'NONE';
403}
404
405export interface LoadedConfigAutomatic {
406  type: 'AUTOMATIC';
407}
408
409export interface LoadedConfigNamed {
410  type: 'NAMED';
411  name: string;
412}
413
414export type LoadedConfig =
415  | LoadedConfigNone
416  | LoadedConfigAutomatic
417  | LoadedConfigNamed;
418
419export interface NonSerializableState {
420  pivotTable: PivotTableState;
421}
422
423export interface PendingDeeplinkState {
424  ts?: string;
425  dur?: string;
426  tid?: string;
427  pid?: string;
428  query?: string;
429  visStart?: string;
430  visEnd?: string;
431}
432
433export interface TabsV2State {
434  openTabs: string[];
435  currentTab: string;
436}
437
438export interface State {
439  version: number;
440  nextId: string;
441
442  /**
443   * State of the ConfigEditor.
444   */
445  recordConfig: RecordConfig;
446  displayConfigAsPbtxt: boolean;
447  lastLoadedConfig: LoadedConfig;
448
449  /**
450   * Open traces.
451   */
452  newEngineMode: NewEngineMode;
453  engine?: EngineConfig;
454  traceUuid?: string;
455  trackGroups: ObjectByKey<TrackGroupState>;
456  tracks: ObjectByKey<TrackState>;
457  utidToThreadSortKey: UtidToTrackSortKey;
458  aggregatePreferences: ObjectById<AggregationState>;
459  scrollingTracks: string[];
460  pinnedTracks: string[];
461  debugTrackId?: string;
462  lastTrackReloadRequest?: number;
463  queries: ObjectById<QueryConfig>;
464  notes: ObjectById<Note | SpanNote>;
465  status: Status;
466  selection: Selection;
467  traceConversionInProgress: boolean;
468  flamegraphModalDismissed: boolean;
469
470  /**
471   * This state is updated on the frontend at 60Hz and eventually syncronised to
472   * the controller at 10Hz. When the controller sends state updates to the
473   * frontend the frontend has special logic to pick whichever version of this
474   * key is most up to date.
475   */
476  frontendLocalState: FrontendLocalState;
477
478  // Show track perf debugging overlay
479  perfDebug: boolean;
480
481  // Show the sidebar extended
482  sidebarVisible: boolean;
483
484  // Hovered and focused events
485  hoveredUtid: number;
486  hoveredPid: number;
487  hoverCursorTimestamp: time;
488  hoveredNoteTimestamp: time;
489  highlightedSliceId: number;
490  focusedFlowIdLeft: number;
491  focusedFlowIdRight: number;
492  pendingScrollId?: number;
493
494  searchIndex: number;
495
496  tabs: TabsV2State;
497
498  /**
499   * Trace recording
500   */
501  recordingInProgress: boolean;
502  recordingCancelled: boolean;
503  extensionInstalled: boolean;
504  recordingTarget: RecordingTarget;
505  availableAdbDevices: AdbRecordingTarget[];
506  lastRecordingError?: string;
507  recordingStatus?: string;
508
509  fetchChromeCategories: boolean;
510  chromeCategories: string[] | undefined;
511
512  // Special key: this part of the state is not going to be serialized when
513  // using permalink. Can be used to store those parts of the state that can't
514  // be serialized at the moment, such as ES6 Set and Map.
515  nonSerializableState: NonSerializableState;
516
517  // Omnibox info.
518  omniboxState: OmniboxState;
519
520  // Pending deeplink which will happen when we first finish opening a
521  // trace.
522  pendingDeeplink?: PendingDeeplinkState;
523
524  // Individual plugin states
525  // eslint-disable-next-line @typescript-eslint/no-explicit-any
526  plugins: {[key: string]: any};
527}
528
529export declare type RecordMode =
530  | 'STOP_WHEN_FULL'
531  | 'RING_BUFFER'
532  | 'LONG_TRACE';
533
534// 'Q','P','O' for Android, 'L' for Linux, 'C' for Chrome.
535export declare type TargetOs =
536  | 'S'
537  | 'R'
538  | 'Q'
539  | 'P'
540  | 'O'
541  | 'C'
542  | 'L'
543  | 'CrOS'
544  | 'Win';
545
546export function isAndroidP(target: RecordingTarget) {
547  return target.os === 'P';
548}
549
550export function isAndroidTarget(target: RecordingTarget) {
551  return ['Q', 'P', 'O'].includes(target.os);
552}
553
554export function isChromeTarget(target: RecordingTarget) {
555  return ['C', 'CrOS'].includes(target.os);
556}
557
558export function isCrOSTarget(target: RecordingTarget) {
559  return target.os === 'CrOS';
560}
561
562export function isLinuxTarget(target: RecordingTarget) {
563  return target.os === 'L';
564}
565
566export function isWindowsTarget(target: RecordingTarget) {
567  return target.os === 'Win';
568}
569
570export function isAdbTarget(
571  target: RecordingTarget,
572): target is AdbRecordingTarget {
573  return !!(target as AdbRecordingTarget).serial;
574}
575
576export function hasActiveProbes(config: RecordConfig) {
577  const fieldsWithEmptyResult = new Set<string>([
578    'hpBlockClient',
579    'allAtraceApps',
580    'chromePrivacyFiltering',
581  ]);
582  let key: keyof RecordConfig;
583  for (key in config) {
584    if (
585      typeof config[key] === 'boolean' &&
586      config[key] === true &&
587      !fieldsWithEmptyResult.has(key)
588    ) {
589      return true;
590    }
591  }
592  if (config.chromeCategoriesSelected.length > 0) {
593    return true;
594  }
595  return config.chromeHighOverheadCategoriesSelected.length > 0;
596}
597
598export function getDefaultRecordingTargets(): RecordingTarget[] {
599  return [
600    {os: 'Q', name: 'Android Q+ / 10+'},
601    {os: 'P', name: 'Android P / 9'},
602    {os: 'O', name: 'Android O- / 8-'},
603    {os: 'C', name: 'Chrome'},
604    {os: 'CrOS', name: 'Chrome OS (system trace)'},
605    {os: 'L', name: 'Linux desktop'},
606    {os: 'Win', name: 'Windows desktop'},
607  ];
608}
609
610export function getBuiltinChromeCategoryList(): string[] {
611  // List of static Chrome categories, last updated at 2024-05-15 from HEAD of
612  // Chromium's //base/trace_event/builtin_categories.h.
613  return [
614    'accessibility',
615    'AccountFetcherService',
616    'android.adpf',
617    'android.ui.jank',
618    'android_webview',
619    'android_webview.timeline',
620    'aogh',
621    'audio',
622    'base',
623    'benchmark',
624    'blink',
625    'blink.animations',
626    'blink.bindings',
627    'blink.console',
628    'blink.net',
629    'blink.resource',
630    'blink.user_timing',
631    'blink.worker',
632    'blink_style',
633    'Blob',
634    'browser',
635    'browsing_data',
636    'CacheStorage',
637    'Calculators',
638    'CameraStream',
639    'cppgc',
640    'camera',
641    'cast_app',
642    'cast_perf_test',
643    'cast.mdns',
644    'cast.mdns.socket',
645    'cast.stream',
646    'cc',
647    'cc.debug',
648    'cdp.perf',
649    'chromeos',
650    'cma',
651    'compositor',
652    'content',
653    'content_capture',
654    'interactions',
655    'delegated_ink_trails',
656    'device',
657    'devtools',
658    'devtools.contrast',
659    'devtools.timeline',
660    'disk_cache',
661    'download',
662    'download_service',
663    'drm',
664    'drmcursor',
665    'dwrite',
666    'DXVA_Decoding',
667    'evdev',
668    'event',
669    'event_latency',
670    'exo',
671    'extensions',
672    'explore_sites',
673    'FileSystem',
674    'file_system_provider',
675    'fledge',
676    'fonts',
677    'GAMEPAD',
678    'gpu',
679    'gpu.angle',
680    'gpu.angle.texture_metrics',
681    'gpu.capture',
682    'graphics.pipeline',
683    'headless',
684    'history',
685    'hwoverlays',
686    'identity',
687    'ime',
688    'IndexedDB',
689    'input',
690    'input.scrolling',
691    'io',
692    'ipc',
693    'Java',
694    'jni',
695    'jpeg',
696    'latency',
697    'latencyInfo',
698    'leveldb',
699    'loading',
700    'log',
701    'login',
702    'media',
703    'media_router',
704    'memory',
705    'midi',
706    'mojom',
707    'mus',
708    'native',
709    'navigation',
710    'navigation.debug',
711    'net',
712    'network.scheduler',
713    'netlog',
714    'offline_pages',
715    'omnibox',
716    'oobe',
717    'openscreen',
718    'ozone',
719    'partition_alloc',
720    'passwords',
721    'p2p',
722    'page-serialization',
723    'paint_preview',
724    'pepper',
725    'PlatformMalloc',
726    'power',
727    'ppapi',
728    'ppapi_proxy',
729    'print',
730    'raf_investigation',
731    'rail',
732    'renderer',
733    'renderer_host',
734    'renderer.scheduler',
735    'resources',
736    'RLZ',
737    'ServiceWorker',
738    'SiteEngagement',
739    'safe_browsing',
740    'scheduler',
741    'scheduler.long_tasks',
742    'screenlock_monitor',
743    'segmentation_platform',
744    'sequence_manager',
745    'service_manager',
746    'sharing',
747    'shell',
748    'shortcut_viewer',
749    'shutdown',
750    'skia',
751    'sql',
752    'stadia_media',
753    'stadia_rtc',
754    'startup',
755    'sync',
756    'system_apps',
757    'test_gpu',
758    'toplevel',
759    'toplevel.flow',
760    'ui',
761    'v8',
762    'v8.execute',
763    'v8.wasm',
764    'ValueStoreFrontend::Backend',
765    'views',
766    'views.frame',
767    'viz',
768    'vk',
769    'wakeup.flow',
770    'wayland',
771    'webaudio',
772    'webengine.fidl',
773    'weblayer',
774    'WebCore',
775    'webnn',
776    'webrtc',
777    'webrtc_stats',
778    'xr',
779    'disabled-by-default-android_view_hierarchy',
780    'disabled-by-default-animation-worklet',
781    'disabled-by-default-audio',
782    'disabled-by-default-audio.latency',
783    'disabled-by-default-audio-worklet',
784    'disabled-by-default-base',
785    'disabled-by-default-blink.debug',
786    'disabled-by-default-blink.debug.display_lock',
787    'disabled-by-default-blink.debug.layout',
788    'disabled-by-default-blink.debug.layout.trees',
789    'disabled-by-default-blink.feature_usage',
790    'disabled-by-default-blink.image_decoding',
791    'disabled-by-default-blink.invalidation',
792    'disabled-by-default-identifiability',
793    'disabled-by-default-identifiability.high_entropy_api',
794    'disabled-by-default-cc',
795    'disabled-by-default-cc.debug',
796    'disabled-by-default-cc.debug.cdp-perf',
797    'disabled-by-default-cc.debug.display_items',
798    'disabled-by-default-cc.debug.lcd_text',
799    'disabled-by-default-cc.debug.picture',
800    'disabled-by-default-cc.debug.scheduler',
801    'disabled-by-default-cc.debug.scheduler.frames',
802    'disabled-by-default-cc.debug.scheduler.now',
803    'disabled-by-default-content.verbose',
804    'disabled-by-default-cpu_profiler',
805    'disabled-by-default-cppgc',
806    'disabled-by-default-cpu_profiler.debug',
807    'disabled-by-default-devtools.screenshot',
808    'disabled-by-default-devtools.timeline',
809    'disabled-by-default-devtools.timeline.frame',
810    'disabled-by-default-devtools.timeline.inputs',
811    'disabled-by-default-devtools.timeline.invalidationTracking',
812    'disabled-by-default-devtools.timeline.layers',
813    'disabled-by-default-devtools.timeline.picture',
814    'disabled-by-default-devtools.timeline.stack',
815    'disabled-by-default-devtools.target-rundown',
816    'disabled-by-default-devtools.v8-source-rundown',
817    'disabled-by-default-devtools.v8-source-rundown-sources',
818    'disabled-by-default-file',
819    'disabled-by-default-fonts',
820    'disabled-by-default-gpu_cmd_queue',
821    'disabled-by-default-gpu.dawn',
822    'disabled-by-default-gpu.debug',
823    'disabled-by-default-gpu.decoder',
824    'disabled-by-default-gpu.device',
825    'disabled-by-default-gpu.graphite.dawn',
826    'disabled-by-default-gpu.service',
827    'disabled-by-default-gpu.vulkan.vma',
828    'disabled-by-default-histogram_samples',
829    'disabled-by-default-java-heap-profiler',
830    'disabled-by-default-layer-element',
831    'disabled-by-default-layout_shift.debug',
832    'disabled-by-default-lifecycles',
833    'disabled-by-default-loading',
834    'disabled-by-default-mediastream',
835    'disabled-by-default-memory-infra',
836    'disabled-by-default-memory-infra.v8.code_stats',
837    'disabled-by-default-mojom',
838    'disabled-by-default-net',
839    'disabled-by-default-network',
840    'disabled-by-default-paint-worklet',
841    'disabled-by-default-power',
842    'disabled-by-default-renderer.scheduler',
843    'disabled-by-default-renderer.scheduler.debug',
844    'disabled-by-default-sequence_manager',
845    'disabled-by-default-sequence_manager.debug',
846    'disabled-by-default-sequence_manager.verbose_snapshots',
847    'disabled-by-default-skia',
848    'disabled-by-default-skia.gpu',
849    'disabled-by-default-skia.gpu.cache',
850    'disabled-by-default-skia.shaders',
851    'disabled-by-default-skottie',
852    'disabled-by-default-SyncFileSystem',
853    'disabled-by-default-system_power',
854    'disabled-by-default-system_stats',
855    'disabled-by-default-thread_pool_diagnostics',
856    'disabled-by-default-toplevel.ipc',
857    'disabled-by-default-user_action_samples',
858    'disabled-by-default-v8.compile',
859    'disabled-by-default-v8.cpu_profiler',
860    'disabled-by-default-v8.gc',
861    'disabled-by-default-v8.gc_stats',
862    'disabled-by-default-v8.ic_stats',
863    'disabled-by-default-v8.inspector',
864    'disabled-by-default-v8.runtime',
865    'disabled-by-default-v8.runtime_stats',
866    'disabled-by-default-v8.runtime_stats_sampling',
867    'disabled-by-default-v8.stack_trace',
868    'disabled-by-default-v8.turbofan',
869    'disabled-by-default-v8.wasm.detailed',
870    'disabled-by-default-v8.wasm.turbofan',
871    'disabled-by-default-video_and_image_capture',
872    'disabled-by-default-display.framedisplayed',
873    'disabled-by-default-viz.gpu_composite_time',
874    'disabled-by-default-viz.debug.overlay_planes',
875    'disabled-by-default-viz.hit_testing_flow',
876    'disabled-by-default-viz.overdraw',
877    'disabled-by-default-viz.quads',
878    'disabled-by-default-viz.surface_id_flow',
879    'disabled-by-default-viz.surface_lifetime',
880    'disabled-by-default-viz.triangles',
881    'disabled-by-default-viz.visual_debugger',
882    'disabled-by-default-webaudio.audionode',
883    'disabled-by-default-webgpu',
884    'disabled-by-default-webnn',
885    'disabled-by-default-webrtc',
886    'disabled-by-default-worker.scheduler',
887    'disabled-by-default-xr.debug',
888  ];
889}
890
891export function getContainingGroupKey(
892  state: State,
893  trackKey: string,
894): null | string {
895  const track = state.tracks[trackKey];
896  if (track === undefined) {
897    return null;
898  }
899  const parentGroupKey = track.trackGroup;
900  if (!parentGroupKey) {
901    return null;
902  }
903  return parentGroupKey;
904}
905
906export function getLegacySelection(state: State): LegacySelection | null {
907  return selectionToLegacySelection(state.selection);
908}
909