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