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 15/** 16 * A plain js object, holding objects of type |Class| keyed by string id. 17 * We use this instead of using |Map| object since it is simpler and faster to 18 * serialize for use in postMessage. 19 */ 20export interface ObjectById<Class extends{id: string}> { [id: string]: Class; } 21 22export type Timestamped<T> = { 23 [P in keyof T]: T[P]; 24}&{lastUpdate: number}; 25 26export type OmniboxState = 27 Timestamped<{omnibox: string; mode: 'SEARCH' | 'COMMAND'}>; 28 29export type VisibleState = 30 Timestamped<{startSec: number; endSec: number; resolution: number;}>; 31 32export interface AreaSelection { 33 kind: 'AREA'; 34 areaId: string; 35 // When an area is marked it will be assigned a unique note id and saved as 36 // an AreaNote for the user to return to later. id = 0 is the special id that 37 // is overwritten when a new area is marked. Any other id is a persistent 38 // marking that will not be overwritten. 39 // When not set, the area selection will be replaced with any 40 // new area selection (i.e. not saved anywhere). 41 noteId?: string; 42} 43 44export type AreaById = Area&{id: string}; 45 46export interface Area { 47 startSec: number; 48 endSec: number; 49 tracks: string[]; 50} 51 52export const MAX_TIME = 180; 53 54// 3: TrackKindPriority and related sorting changes. 55export const STATE_VERSION = 3; 56 57export const SCROLLING_TRACK_GROUP = 'ScrollingTracks'; 58 59export type EngineMode = 'WASM'|'HTTP_RPC'; 60 61export type NewEngineMode = 'USE_HTTP_RPC_IF_AVAILABLE'|'FORCE_BUILTIN_WASM'; 62 63export enum TrackKindPriority { 64 'MAIN_THREAD' = 0, 65 'RENDER_THREAD' = 1, 66 'GPU_COMPLETION' = 2, 67 'ORDINARY' = 3 68} 69 70export type HeapProfileFlamegraphViewingOption = 71 'SPACE'|'ALLOC_SPACE'|'OBJECTS'|'ALLOC_OBJECTS'; 72 73export interface CallsiteInfo { 74 id: number; 75 parentId: number; 76 depth: number; 77 name?: string; 78 totalSize: number; 79 selfSize: number; 80 mapping: string; 81 merged: boolean; 82 highlighted: boolean; 83} 84 85export interface TraceFileSource { 86 type: 'FILE'; 87 file: File; 88} 89 90export interface TraceArrayBufferSource { 91 type: 'ARRAY_BUFFER'; 92 title: string; 93 url?: string; 94 fileName?: string; 95 buffer: ArrayBuffer; 96} 97 98export interface TraceUrlSource { 99 type: 'URL'; 100 url: string; 101} 102 103export interface TraceHttpRpcSource { 104 type: 'HTTP_RPC'; 105} 106 107export type TraceSource = 108 TraceFileSource|TraceArrayBufferSource|TraceUrlSource|TraceHttpRpcSource; 109 110export interface TrackState { 111 id: string; 112 engineId: string; 113 kind: string; 114 name: string; 115 trackKindPriority: TrackKindPriority; 116 trackGroup?: string; 117 config: {}; 118} 119 120export interface TrackGroupState { 121 id: string; 122 engineId: string; 123 name: string; 124 collapsed: boolean; 125 tracks: string[]; // Child track ids. 126} 127 128export interface EngineConfig { 129 id: string; 130 mode?: EngineMode; // Is undefined until |ready| is true. 131 ready: boolean; 132 failed?: string; // If defined the engine has crashed with the given message. 133 source: TraceSource; 134} 135 136export interface QueryConfig { 137 id: string; 138 engineId: string; 139 query: string; 140} 141 142export interface PermalinkConfig { 143 requestId?: string; // Set by the frontend to request a new permalink. 144 hash?: string; // Set by the controller when the link has been created. 145 isRecordingConfig?: 146 boolean; // this permalink request is for a recording config only 147} 148 149export interface TraceTime { 150 startSec: number; 151 endSec: number; 152} 153 154export interface FrontendLocalState { 155 omniboxState: OmniboxState; 156 visibleState: VisibleState; 157} 158 159export interface Status { 160 msg: string; 161 timestamp: number; // Epoch in seconds (Date.now() / 1000). 162} 163 164export interface Note { 165 noteType: 'DEFAULT'|'MOVIE'; 166 id: string; 167 timestamp: number; 168 color: string; 169 text: string; 170} 171 172export interface AreaNote { 173 noteType: 'AREA'; 174 id: string; 175 areaId: string; 176 color: string; 177 text: string; 178} 179 180export interface NoteSelection { 181 kind: 'NOTE'; 182 id: string; 183} 184 185export interface SliceSelection { 186 kind: 'SLICE'; 187 id: number; 188} 189 190export interface CounterSelection { 191 kind: 'COUNTER'; 192 leftTs: number; 193 rightTs: number; 194 id: number; 195} 196 197export interface HeapProfileSelection { 198 kind: 'HEAP_PROFILE'; 199 id: number; 200 upid: number; 201 ts: number; 202 type: string; 203} 204 205export interface HeapProfileFlamegraph { 206 kind: 'HEAP_PROFILE_FLAMEGRAPH'; 207 id: number; 208 upid: number; 209 ts: number; 210 type: string; 211 viewingOption: HeapProfileFlamegraphViewingOption; 212 focusRegex: string; 213 expandedCallsite?: CallsiteInfo; 214} 215 216export interface CpuProfileSampleSelection { 217 kind: 'CPU_PROFILE_SAMPLE'; 218 id: number; 219 utid: number; 220 ts: number; 221} 222 223export interface ChromeSliceSelection { 224 kind: 'CHROME_SLICE'; 225 id: number; 226 table: string; 227} 228 229export interface ThreadStateSelection { 230 kind: 'THREAD_STATE'; 231 id: number; 232} 233 234type Selection = 235 (NoteSelection|SliceSelection|CounterSelection|HeapProfileSelection| 236 CpuProfileSampleSelection|ChromeSliceSelection|ThreadStateSelection| 237 AreaSelection)&{trackId?: string}; 238 239export interface LogsPagination { 240 offset: number; 241 count: number; 242} 243 244export interface RecordingTarget { 245 name: string; 246 os: TargetOs; 247} 248 249export interface AdbRecordingTarget extends RecordingTarget { 250 serial: string; 251} 252 253export interface Sorting { 254 column: string; 255 direction: 'DESC'|'ASC'; 256} 257 258export interface AggregationState { 259 id: string; 260 sorting?: Sorting; 261} 262 263export interface MetricsState { 264 availableMetrics?: string[]; // Undefined until list is loaded. 265 selectedIndex?: number; 266 requestedMetric?: string; // Unset after metric request is handled. 267} 268 269export interface State { 270 // tslint:disable-next-line:no-any 271 [key: string]: any; 272 version: number; 273 route: string|null; 274 nextId: number; 275 nextNoteId: number; 276 nextAreaId: number; 277 278 /** 279 * State of the ConfigEditor. 280 */ 281 recordConfig: RecordConfig; 282 displayConfigAsPbtxt: boolean; 283 284 /** 285 * Open traces. 286 */ 287 newEngineMode: NewEngineMode; 288 engines: ObjectById<EngineConfig>; 289 traceTime: TraceTime; 290 trackGroups: ObjectById<TrackGroupState>; 291 tracks: ObjectById<TrackState>; 292 areas: ObjectById<AreaById>; 293 aggregatePreferences: ObjectById<AggregationState>; 294 visibleTracks: string[]; 295 scrollingTracks: string[]; 296 pinnedTracks: string[]; 297 debugTrackId?: string; 298 lastTrackReloadRequest?: number; 299 queries: ObjectById<QueryConfig>; 300 metrics: MetricsState; 301 permalink: PermalinkConfig; 302 notes: ObjectById<Note|AreaNote>; 303 status: Status; 304 currentSelection: Selection|null; 305 currentHeapProfileFlamegraph: HeapProfileFlamegraph|null; 306 logsPagination: LogsPagination; 307 traceConversionInProgress: boolean; 308 309 /** 310 * This state is updated on the frontend at 60Hz and eventually syncronised to 311 * the controller at 10Hz. When the controller sends state updates to the 312 * frontend the frontend has special logic to pick whichever version of this 313 * key is most up to date. 314 */ 315 frontendLocalState: FrontendLocalState; 316 317 video: string | null; 318 videoEnabled: boolean; 319 videoOffset: number; 320 videoNoteIds: string[]; 321 scrubbingEnabled: boolean; 322 flagPauseEnabled: boolean; 323 324 /** 325 * Trace recording 326 */ 327 recordingInProgress: boolean; 328 recordingCancelled: boolean; 329 extensionInstalled: boolean; 330 recordingTarget: RecordingTarget; 331 availableAdbDevices: AdbRecordingTarget[]; 332 lastRecordingError?: string; 333 recordingStatus?: string; 334 335 updateChromeCategories: boolean; 336 chromeCategories: string[]|undefined; 337 analyzePageQuery?: string; 338} 339 340export const defaultTraceTime = { 341 startSec: 0, 342 endSec: 10, 343}; 344 345export declare type RecordMode = 346 'STOP_WHEN_FULL' | 'RING_BUFFER' | 'LONG_TRACE'; 347 348// 'Q','P','O' for Android, 'L' for Linux, 'C' for Chrome. 349export declare type TargetOs = 'Q' | 'P' | 'O' | 'C' | 'L' | 'CrOS'; 350 351export function isAndroidP(target: RecordingTarget) { 352 return target.os === 'P'; 353} 354 355export function isAndroidTarget(target: RecordingTarget) { 356 return ['Q', 'P', 'O'].includes(target.os); 357} 358 359export function isChromeTarget(target: RecordingTarget) { 360 return ['C', 'CrOS'].includes(target.os); 361} 362 363export function isCrOSTarget(target: RecordingTarget) { 364 return target.os === 'CrOS'; 365} 366 367export function isLinuxTarget(target: RecordingTarget) { 368 return target.os === 'L'; 369} 370 371export function isAdbTarget(target: RecordingTarget): 372 target is AdbRecordingTarget { 373 if ((target as AdbRecordingTarget).serial) return true; 374 return false; 375} 376 377export function hasActiveProbes(config: RecordConfig) { 378 const fieldsWithEmptyResult = new Set<string>(['hpBlockClient']); 379 for (const key in config) { 380 if (typeof (config[key]) === 'boolean' && config[key] === true && 381 !fieldsWithEmptyResult.has(key)) { 382 return true; 383 } 384 } 385 return false; 386} 387 388export interface RecordConfig { 389 [key: string]: null|number|boolean|string|string[]; 390 391 // Global settings 392 mode: RecordMode; 393 durationMs: number; 394 bufferSizeMb: number; 395 maxFileSizeMb: number; // Only for mode == 'LONG_TRACE'. 396 fileWritePeriodMs: number; // Only for mode == 'LONG_TRACE'. 397 398 cpuSched: boolean; 399 cpuFreq: boolean; 400 cpuCoarse: boolean; 401 cpuCoarsePollMs: number; 402 cpuSyscall: boolean; 403 404 screenRecord: boolean; 405 406 gpuFreq: boolean; 407 gpuMemTotal: boolean; 408 409 ftrace: boolean; 410 atrace: boolean; 411 ftraceEvents: string[]; 412 ftraceExtraEvents: string; 413 atraceCats: string[]; 414 atraceApps: string; 415 ftraceBufferSizeKb: number; 416 ftraceDrainPeriodMs: number; 417 androidLogs: boolean; 418 androidLogBuffers: string[]; 419 androidFrameTimeline: boolean; 420 421 batteryDrain: boolean; 422 batteryDrainPollMs: number; 423 424 boardSensors: boolean; 425 426 memHiFreq: boolean; 427 memLmk: boolean; 428 meminfo: boolean; 429 meminfoPeriodMs: number; 430 meminfoCounters: string[]; 431 vmstat: boolean; 432 vmstatPeriodMs: number; 433 vmstatCounters: string[]; 434 435 heapProfiling: boolean; 436 hpSamplingIntervalBytes: number; 437 hpProcesses: string; 438 hpContinuousDumpsPhase: number; 439 hpContinuousDumpsInterval: number; 440 hpSharedMemoryBuffer: number; 441 hpBlockClient: boolean; 442 hpAllHeaps: boolean; 443 444 javaHeapDump: boolean; 445 jpProcesses: string; 446 jpContinuousDumpsPhase: number; 447 jpContinuousDumpsInterval: number; 448 449 procStats: boolean; 450 procStatsPeriodMs: number; 451 452 chromeCategoriesSelected: string[]; 453} 454 455export function createEmptyRecordConfig(): RecordConfig { 456 return { 457 mode: 'STOP_WHEN_FULL', 458 durationMs: 10000.0, 459 maxFileSizeMb: 100, 460 fileWritePeriodMs: 2500, 461 bufferSizeMb: 64.0, 462 463 cpuSched: false, 464 cpuFreq: false, 465 cpuSyscall: false, 466 467 screenRecord: false, 468 469 gpuFreq: false, 470 gpuMemTotal: false, 471 472 ftrace: false, 473 atrace: false, 474 ftraceEvents: [], 475 ftraceExtraEvents: '', 476 atraceCats: [], 477 atraceApps: '', 478 ftraceBufferSizeKb: 2 * 1024, 479 ftraceDrainPeriodMs: 250, 480 androidLogs: false, 481 androidLogBuffers: [], 482 androidFrameTimeline: false, 483 484 cpuCoarse: false, 485 cpuCoarsePollMs: 1000, 486 487 batteryDrain: false, 488 batteryDrainPollMs: 1000, 489 490 boardSensors: false, 491 492 memHiFreq: false, 493 meminfo: false, 494 meminfoPeriodMs: 1000, 495 meminfoCounters: [], 496 497 vmstat: false, 498 vmstatPeriodMs: 1000, 499 vmstatCounters: [], 500 501 heapProfiling: false, 502 hpSamplingIntervalBytes: 4096, 503 hpProcesses: '', 504 hpContinuousDumpsPhase: 0, 505 hpContinuousDumpsInterval: 0, 506 hpSharedMemoryBuffer: 8 * 1048576, 507 hpBlockClient: true, 508 hpAllHeaps: false, 509 510 javaHeapDump: false, 511 jpProcesses: '', 512 jpContinuousDumpsPhase: 0, 513 jpContinuousDumpsInterval: 0, 514 515 memLmk: false, 516 procStats: false, 517 procStatsPeriodMs: 1000, 518 519 chromeCategoriesSelected: [], 520 }; 521} 522 523export function getDefaultRecordingTargets(): RecordingTarget[] { 524 return [ 525 {os: 'Q', name: 'Android Q+'}, 526 {os: 'P', name: 'Android P'}, 527 {os: 'O', name: 'Android O-'}, 528 {os: 'C', name: 'Chrome'}, 529 {os: 'CrOS', name: 'Chrome OS (system trace)'}, 530 {os: 'L', name: 'Linux desktop'} 531 ]; 532} 533 534export function getBuiltinChromeCategoryList(): string[] { 535 // List of static Chrome categories, last updated at Chromium 81.0.4021.0 from 536 // Chromium's //base/trace_event/builtin_categories.h. 537 return [ 538 'accessibility', 539 'AccountFetcherService', 540 'android_webview', 541 'aogh', 542 'audio', 543 'base', 544 'benchmark', 545 'blink', 546 'blink.animations', 547 'blink.bindings', 548 'blink.console', 549 'blink_gc', 550 'blink.net', 551 'blink_style', 552 'blink.user_timing', 553 'blink.worker', 554 'Blob', 555 'browser', 556 'browsing_data', 557 'CacheStorage', 558 'Calculators', 559 'CameraStream', 560 'camera', 561 'cast_app', 562 'cast_perf_test', 563 'cast.mdns', 564 'cast.mdns.socket', 565 'cast.stream', 566 'cc', 567 'cc.debug', 568 'cdp.perf', 569 'chromeos', 570 'cma', 571 'compositor', 572 'content', 573 'content_capture', 574 'device', 575 'devtools', 576 'devtools.contrast', 577 'devtools.timeline', 578 'disk_cache', 579 'download', 580 'download_service', 581 'drm', 582 'drmcursor', 583 'dwrite', 584 'DXVA_Decoding', 585 'EarlyJava', 586 'evdev', 587 'event', 588 'exo', 589 'explore_sites', 590 'FileSystem', 591 'file_system_provider', 592 'fonts', 593 'GAMEPAD', 594 'gpu', 595 'gpu.angle', 596 'gpu.capture', 597 'headless', 598 'hwoverlays', 599 'identity', 600 'ime', 601 'IndexedDB', 602 'input', 603 'io', 604 'ipc', 605 'Java', 606 'jni', 607 'jpeg', 608 'latency', 609 'latencyInfo', 610 'leveldb', 611 'loading', 612 'log', 613 'login', 614 'media', 615 'media_router', 616 'memory', 617 'midi', 618 'mojom', 619 'mus', 620 'native', 621 'navigation', 622 'net', 623 'netlog', 624 'offline_pages', 625 'omnibox', 626 'oobe', 627 'ozone', 628 'partition_alloc', 629 'passwords', 630 'p2p', 631 'page-serialization', 632 'paint_preview', 633 'pepper', 634 'PlatformMalloc', 635 'power', 636 'ppapi', 637 'ppapi_proxy', 638 'print', 639 'rail', 640 'renderer', 641 'renderer_host', 642 'renderer.scheduler', 643 'RLZ', 644 'safe_browsing', 645 'screenlock_monitor', 646 'sequence_manager', 647 'service_manager', 648 'ServiceWorker', 649 'sharing', 650 'shell', 651 'shortcut_viewer', 652 'shutdown', 653 'SiteEngagement', 654 'skia', 655 'sql', 656 'stadia_media', 657 'stadia_rtc', 658 'startup', 659 'sync', 660 'sync_lock_contention', 661 'test_gpu', 662 'thread_pool', 663 'toplevel', 664 'toplevel.flow', 665 'ui', 666 'v8', 667 'v8.execute', 668 'v8.wasm', 669 'ValueStoreFrontend::Backend', 670 'views', 671 'views.frame', 672 'viz', 673 'vk', 674 'wayland', 675 'webaudio', 676 'weblayer', 677 'WebCore', 678 'webrtc', 679 'xr', 680 'disabled-by-default-animation-worklet', 681 'disabled-by-default-audio', 682 'disabled-by-default-audio-worklet', 683 'disabled-by-default-blink.debug', 684 'disabled-by-default-blink.debug.display_lock', 685 'disabled-by-default-blink.debug.layout', 686 'disabled-by-default-blink.debug.layout.trees', 687 'disabled-by-default-blink.feature_usage', 688 'disabled-by-default-blink_gc', 689 'disabled-by-default-blink.image_decoding', 690 'disabled-by-default-blink.invalidation', 691 'disabled-by-default-cc', 692 'disabled-by-default-cc.debug', 693 'disabled-by-default-cc.debug.cdp-perf', 694 'disabled-by-default-cc.debug.display_items', 695 'disabled-by-default-cc.debug.picture', 696 'disabled-by-default-cc.debug.scheduler', 697 'disabled-by-default-cc.debug.scheduler.frames', 698 'disabled-by-default-cc.debug.scheduler.now', 699 'disabled-by-default-content.verbose', 700 'disabled-by-default-cpu_profiler', 701 'disabled-by-default-cpu_profiler.debug', 702 'disabled-by-default-devtools.screenshot', 703 'disabled-by-default-devtools.timeline', 704 'disabled-by-default-devtools.timeline.frame', 705 'disabled-by-default-devtools.timeline.inputs', 706 'disabled-by-default-devtools.timeline.invalidationTracking', 707 'disabled-by-default-devtools.timeline.layers', 708 'disabled-by-default-devtools.timeline.picture', 709 'disabled-by-default-file', 710 'disabled-by-default-fonts', 711 'disabled-by-default-gpu_cmd_queue', 712 'disabled-by-default-gpu.dawn', 713 'disabled-by-default-gpu.debug', 714 'disabled-by-default-gpu.decoder', 715 'disabled-by-default-gpu.device', 716 'disabled-by-default-gpu.service', 717 'disabled-by-default-gpu.vulkan.vma', 718 'disabled-by-default-histogram_samples', 719 'disabled-by-default-ipc.flow', 720 'disabled-by-default-java-heap-profiler', 721 'disabled-by-default-layer-element', 722 'disabled-by-default-layout_shift.debug', 723 'disabled-by-default-lifecycles', 724 'disabled-by-default-loading', 725 'disabled-by-default-mediastream', 726 'disabled-by-default-memory-infra', 727 'disabled-by-default-memory-infra.v8.code_stats', 728 'disabled-by-default-mojom', 729 'disabled-by-default-net', 730 'disabled-by-default-network', 731 'disabled-by-default-paint-worklet', 732 'disabled-by-default-power', 733 'disabled-by-default-renderer.scheduler', 734 'disabled-by-default-renderer.scheduler.debug', 735 'disabled-by-default-sandbox', 736 'disabled-by-default-sequence_manager', 737 'disabled-by-default-sequence_manager.debug', 738 'disabled-by-default-sequence_manager.verbose_snapshots', 739 'disabled-by-default-skia', 740 'disabled-by-default-skia.gpu', 741 'disabled-by-default-skia.gpu.cache', 742 'disabled-by-default-skia.shaders', 743 'disabled-by-default-SyncFileSystem', 744 'disabled-by-default-system_stats', 745 'disabled-by-default-thread_pool_diagnostics', 746 'disabled-by-default-toplevel.flow', 747 'disabled-by-default-toplevel.ipc', 748 'disabled-by-default-user_action_samples', 749 'disabled-by-default-v8.compile', 750 'disabled-by-default-v8.cpu_profiler', 751 'disabled-by-default-v8.cpu_profiler.hires', 752 'disabled-by-default-v8.gc', 753 'disabled-by-default-v8.gc_stats', 754 'disabled-by-default-v8.ic_stats', 755 'disabled-by-default-v8.runtime', 756 'disabled-by-default-v8.runtime_stats', 757 'disabled-by-default-v8.runtime_stats_sampling', 758 'disabled-by-default-v8.stack_trace', 759 'disabled-by-default-v8.turbofan', 760 'disabled-by-default-v8.wasm', 761 'disabled-by-default-v8.wasm.detailed', 762 'disabled-by-default-v8.wasm.turbofan', 763 'disabled-by-default-video_and_image_capture', 764 'disabled-by-default-viz.debug.overlay_planes', 765 'disabled-by-default-viz.gpu_composite_time', 766 'disabled-by-default-viz.hit_testing_flow', 767 'disabled-by-default-viz.overdraw', 768 'disabled-by-default-viz.quads', 769 'disabled-by-default-viz.surface_id_flow', 770 'disabled-by-default-viz.surface_lifetime', 771 'disabled-by-default-viz.triangles', 772 'disabled-by-default-webaudio.audionode', 773 'disabled-by-default-webrtc', 774 'disabled-by-default-worker.scheduler', 775 'disabled-by-default-xr.debug', 776 ]; 777} 778 779export function createEmptyState(): State { 780 return { 781 version: STATE_VERSION, 782 route: null, 783 nextId: 0, 784 nextNoteId: 1, // 0 is reserved for ephemeral area marking. 785 nextAreaId: 0, 786 newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE', 787 engines: {}, 788 traceTime: {...defaultTraceTime}, 789 tracks: {}, 790 aggregatePreferences: {}, 791 trackGroups: {}, 792 visibleTracks: [], 793 pinnedTracks: [], 794 scrollingTracks: [], 795 areas: {}, 796 queries: {}, 797 metrics: {}, 798 permalink: {}, 799 notes: {}, 800 801 recordConfig: createEmptyRecordConfig(), 802 displayConfigAsPbtxt: false, 803 804 frontendLocalState: { 805 omniboxState: { 806 lastUpdate: 0, 807 omnibox: '', 808 mode: 'SEARCH', 809 }, 810 811 visibleState: { 812 ...defaultTraceTime, 813 lastUpdate: 0, 814 resolution: 0, 815 }, 816 }, 817 818 logsPagination: { 819 offset: 0, 820 count: 0, 821 }, 822 823 status: {msg: '', timestamp: 0}, 824 currentSelection: null, 825 currentHeapProfileFlamegraph: null, 826 traceConversionInProgress: false, 827 828 video: null, 829 videoEnabled: false, 830 videoOffset: 0, 831 videoNoteIds: [], 832 scrubbingEnabled: false, 833 flagPauseEnabled: false, 834 recordingInProgress: false, 835 recordingCancelled: false, 836 extensionInstalled: false, 837 recordingTarget: getDefaultRecordingTargets()[0], 838 availableAdbDevices: [], 839 840 updateChromeCategories: false, 841 chromeCategories: undefined, 842 }; 843} 844 845export function getContainingTrackId(state: State, trackId: string): null| 846 string { 847 const track = state.tracks[trackId]; 848 if (!track) { 849 return null; 850 } 851 const parentId = track.trackGroup; 852 if (!parentId) { 853 return null; 854 } 855 return parentId; 856} 857