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