1// Copyright (C) 2024 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 m from 'mithril'; 16import protos from '../../../protos'; 17import { 18 RecordSubpage, 19 RecordProbe, 20 ProbeSetting, 21} from '../config/config_interfaces'; 22import {TraceConfigBuilder} from '../config/trace_config_builder'; 23import {Toggle} from './widgets/toggle'; 24import {Section} from '../../../widgets/section'; 25import { 26 MultiSelect, 27 MultiSelectDiff, 28 MultiSelectOption, 29} from '../../../widgets/multiselect'; 30import {Result} from '../../../base/result'; 31 32type ChromeCatFunction = () => Promise<Result<string[]>>; 33 34export function chromeRecordSection( 35 chromeCategoryGetter: ChromeCatFunction, 36): RecordSubpage { 37 return { 38 kind: 'PROBES_PAGE', 39 id: 'chrome', 40 title: 'Chrome browser', 41 subtitle: 'Chrome tracing', 42 icon: 'laptop_chromebook', 43 probes: [chromeProbe(chromeCategoryGetter)], 44 }; 45} 46 47function chromeProbe(chromeCategoryGetter: ChromeCatFunction): RecordProbe { 48 const groupToggles = Object.fromEntries( 49 Object.keys(GROUPS).map((groupName) => [ 50 groupName, 51 new Toggle({ 52 title: groupName, 53 }), 54 ]), 55 ); 56 const settings = { 57 ...groupToggles, 58 privacy: new Toggle({ 59 title: 'Remove untyped and sensitive data like URLs from the trace', 60 descr: 61 'Not recommended unless you intend to share the trace' + 62 ' with third-parties.', 63 }), 64 categories: new ChromeCategoriesWidget(chromeCategoryGetter), 65 }; 66 return { 67 id: 'chrome_tracing', 68 title: 'Chrome browser tracing', 69 settings, 70 genConfig: function (tc: TraceConfigBuilder) { 71 const cats = new Set<string>(); 72 settings.categories.getEnabledCategories().forEach((c) => cats.add(c)); 73 for (const [group, groupCats] of Object.entries(GROUPS)) { 74 if ((groupToggles[group] as Toggle).enabled) { 75 groupCats.forEach((c) => cats.add(c)); 76 } 77 } 78 const memoryInfra = cats.has('disabled-by-default-memory-infra'); 79 const jsonStruct = { 80 record_mode: 81 tc.mode === 'STOP_WHEN_FULL' 82 ? 'record-until-full' 83 : 'record-continuously', 84 included_categories: [...cats], 85 excluded_categories: ['*'], // Only include categories explicitly 86 memory_dump_config: memoryInfra 87 ? { 88 allowed_dump_modes: ['background', 'light', 'detailed'], 89 triggers: [ 90 { 91 min_time_between_dumps_ms: 10000, 92 mode: 'detailed', 93 type: 'periodic_interval', 94 }, 95 ], 96 } 97 : undefined, 98 }; 99 const privacyFilteringEnabled = settings.privacy.enabled; 100 const chromeConfig = { 101 clientPriority: protos.ChromeConfig.ClientPriority.USER_INITIATED, 102 privacyFilteringEnabled, 103 traceConfig: JSON.stringify(jsonStruct), 104 }; 105 106 const trackEvent = tc.addDataSource('track_event'); 107 trackEvent.chromeConfig = chromeConfig; 108 const trackEvtCfg = (trackEvent.trackEventConfig ??= {}); 109 trackEvtCfg.disabledCategories ??= ['*']; 110 trackEvtCfg.enabledCategories ??= []; 111 trackEvtCfg.enabledCategories.push(...cats); 112 trackEvtCfg.enabledCategories.push('__metadata'); 113 trackEvtCfg.enableThreadTimeSampling = true; 114 trackEvtCfg.timestampUnitMultiplier = 1000; 115 trackEvtCfg.filterDynamicEventNames = privacyFilteringEnabled; 116 trackEvtCfg.filterDebugAnnotations = privacyFilteringEnabled; 117 118 tc.addDataSource('org.chromium.trace_metadata').chromeConfig = 119 chromeConfig; 120 121 if (memoryInfra) { 122 tc.addDataSource('org.chromium.memory_instrumentation').chromeConfig = 123 chromeConfig; 124 tc.addDataSource('org.chromium.native_heap_profiler').chromeConfig = 125 chromeConfig; 126 } 127 128 if ( 129 cats.has('disabled-by-default-cpu_profiler') || 130 cats.has('disabled-by-default-cpu_profiler.debug') 131 ) { 132 tc.addDataSource('org.chromium.sampler_profiler').chromeConfig = 133 chromeConfig; 134 } 135 if (cats.has('disabled-by-default-system_metrics')) { 136 tc.addDataSource('org.chromium.system_metrics').chromeConfig = 137 chromeConfig; 138 } 139 if (cats.has('disabled-by-default-histogram_samples')) { 140 const histogram = tc.addDataSource('org.chromium.histogram_samples'); 141 const histogramCfg = (histogram.chromiumHistogramSamples ??= {}); 142 histogramCfg.filterHistogramNames = privacyFilteringEnabled; 143 } 144 }, 145 }; 146} 147 148const DISAB_PREFIX = 'disabled-by-default-'; 149 150export class ChromeCategoriesWidget implements ProbeSetting { 151 private options = new Array<MultiSelectOption>(); 152 private fetchedRuntimeCategories = false; 153 154 constructor(private chromeCategoryGetter: ChromeCatFunction) { 155 // Initialize first with the static list of builtin categories (in case 156 // something goes wrong with the extension). 157 this.initializeCategories(BUILTIN_CATEGORIES); 158 } 159 160 private async fetchRuntimeCategoriesIfNeeded() { 161 if (this.fetchedRuntimeCategories) return; 162 const runtimeCategories = await this.chromeCategoryGetter(); 163 if (runtimeCategories.ok) { 164 this.initializeCategories(runtimeCategories.value); 165 m.redraw(); 166 } 167 this.fetchedRuntimeCategories = true; 168 } 169 170 private initializeCategories(cats: string[]) { 171 this.options = cats 172 .map((cat) => ({ 173 id: cat, 174 name: cat.replace(DISAB_PREFIX, ''), 175 checked: this.options.find((o) => o.id === cat)?.checked ?? false, 176 })) 177 .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); 178 } 179 180 getEnabledCategories(): string[] { 181 return this.options.filter((o) => o.checked).map((o) => o.id); 182 } 183 184 setEnabled(cat: string, enabled: boolean) { 185 for (const option of this.options) { 186 if (option.id !== cat) continue; 187 option.checked = enabled; 188 } 189 } 190 191 serialize() { 192 return this.options.filter((o) => o.checked).map((o) => o.id); 193 } 194 195 deserialize(state: unknown): void { 196 if (Array.isArray(state) && state.every((x) => typeof x === 'string')) { 197 this.options.forEach((o) => (o.checked = false)); 198 for (const key of state) { 199 const opt = this.options.find((o) => o.id === key); 200 if (opt !== undefined) opt.checked = true; 201 } 202 } 203 } 204 205 render() { 206 return m( 207 'div.chrome-categories', 208 { 209 // This shouldn't be necessary in most cases. It's only needed: 210 // 1. The first time the user installs the extension. 211 // 2. In rare cases if the extension fails to respond to the call in the 212 // constructor, to deal with its flakiness. 213 oninit: () => this.fetchRuntimeCategoriesIfNeeded(), 214 }, 215 m( 216 Section, 217 {title: 'Additional Categories'}, 218 m(MultiSelect, { 219 options: this.options.filter((o) => !o.id.startsWith(DISAB_PREFIX)), 220 repeatCheckedItemsAtTop: false, 221 fixedSize: false, 222 onChange: (diffs: MultiSelectDiff[]) => { 223 diffs.forEach(({id, checked}) => this.setEnabled(id, checked)); 224 }, 225 }), 226 ), 227 m( 228 Section, 229 {title: 'High Overhead Categories'}, 230 m(MultiSelect, { 231 options: this.options.filter((o) => o.id.startsWith(DISAB_PREFIX)), 232 repeatCheckedItemsAtTop: false, 233 fixedSize: false, 234 onChange: (diffs: MultiSelectDiff[]) => { 235 diffs.forEach(({id, checked}) => this.setEnabled(id, checked)); 236 }, 237 }), 238 ), 239 ); 240 } 241} 242 243function defaultAndDisabled(category: string) { 244 return [category, 'disabled-by-default-' + category]; 245} 246 247const GROUPS = { 248 'Task Scheduling': [ 249 'toplevel', 250 'toplevel.flow', 251 'scheduler', 252 'sequence_manager', 253 'disabled-by-default-toplevel.flow', 254 ], 255 'IPC Flows': [ 256 'toplevel', 257 'toplevel.flow', 258 'disabled-by-default-ipc.flow', 259 'mojom', 260 ], 261 'Javascript execution': ['toplevel', 'v8'], 262 'Web content rendering, layout and compositing': [ 263 'toplevel', 264 'blink', 265 'cc', 266 'gpu', 267 ], 268 'UI rendering and surface compositing': [ 269 'toplevel', 270 'cc', 271 'gpu', 272 'viz', 273 'ui', 274 'views', 275 ], 276 'Input events': [ 277 'toplevel', 278 'benchmark', 279 'evdev', 280 'input', 281 'disabled-by-default-toplevel.flow', 282 ], 283 'Navigation and loading': [ 284 'loading', 285 'net', 286 'netlog', 287 'navigation', 288 'browser', 289 ], 290 'Audio': [ 291 'base', 292 ...defaultAndDisabled('audio'), 293 ...defaultAndDisabled('webaudio'), 294 ...defaultAndDisabled('webaudio.audionode'), 295 ...defaultAndDisabled('webrtc'), 296 ...defaultAndDisabled('audio-worklet'), 297 ...defaultAndDisabled('mediastream'), 298 ...defaultAndDisabled('v8.gc'), 299 ...defaultAndDisabled('toplevel'), 300 ...defaultAndDisabled('toplevel.flow'), 301 ...defaultAndDisabled('wakeup.flow'), 302 ...defaultAndDisabled('cpu_profiler'), 303 ...defaultAndDisabled('scheduler'), 304 ...defaultAndDisabled('p2p'), 305 ...defaultAndDisabled('net'), 306 ], 307 'Video': [ 308 'base', 309 'gpu', 310 'gpu.capture', 311 'media', 312 'toplevel', 313 'toplevel.flow', 314 'scheduler', 315 'wakeup.flow', 316 'webrtc', 317 'disabled-by-default-video_and_image_capture', 318 'disabled-by-default-webrtc', 319 ], 320}; 321 322// List of static Chrome categories, last updated at 2024-05-15 from HEAD of 323// Chromium's //base/trace_event/builtin_categories.h. 324const BUILTIN_CATEGORIES = [ 325 'accessibility', 326 'AccountFetcherService', 327 'android.adpf', 328 'android.ui.jank', 329 'android_webview', 330 'android_webview.timeline', 331 'aogh', 332 'audio', 333 'base', 334 'benchmark', 335 'blink', 336 'blink.animations', 337 'blink.bindings', 338 'blink.console', 339 'blink.net', 340 'blink.resource', 341 'blink.user_timing', 342 'blink.worker', 343 'blink_style', 344 'Blob', 345 'browser', 346 'browsing_data', 347 'CacheStorage', 348 'Calculators', 349 'CameraStream', 350 'cppgc', 351 'camera', 352 'cast_app', 353 'cast_perf_test', 354 'cast.mdns', 355 'cast.mdns.socket', 356 'cast.stream', 357 'cc', 358 'cc.debug', 359 'cdp.perf', 360 'chromeos', 361 'cma', 362 'compositor', 363 'content', 364 'content_capture', 365 'interactions', 366 'delegated_ink_trails', 367 'device', 368 'devtools', 369 'devtools.contrast', 370 'devtools.timeline', 371 'disk_cache', 372 'download', 373 'download_service', 374 'drm', 375 'drmcursor', 376 'dwrite', 377 'DXVA_Decoding', 378 'evdev', 379 'event', 380 'event_latency', 381 'exo', 382 'extensions', 383 'explore_sites', 384 'FileSystem', 385 'file_system_provider', 386 'fledge', 387 'fonts', 388 'GAMEPAD', 389 'gpu', 390 'gpu.angle', 391 'gpu.angle.texture_metrics', 392 'gpu.capture', 393 'graphics.pipeline', 394 'headless', 395 'history', 396 'hwoverlays', 397 'identity', 398 'ime', 399 'IndexedDB', 400 'input', 401 'input.scrolling', 402 'io', 403 'ipc', 404 'Java', 405 'jni', 406 'jpeg', 407 'latency', 408 'latencyInfo', 409 'leveldb', 410 'loading', 411 'log', 412 'login', 413 'media', 414 'media_router', 415 'memory', 416 'midi', 417 'mojom', 418 'mus', 419 'native', 420 'navigation', 421 'navigation.debug', 422 'net', 423 'network.scheduler', 424 'netlog', 425 'offline_pages', 426 'omnibox', 427 'oobe', 428 'openscreen', 429 'ozone', 430 'partition_alloc', 431 'passwords', 432 'p2p', 433 'page-serialization', 434 'paint_preview', 435 'pepper', 436 'PlatformMalloc', 437 'power', 438 'ppapi', 439 'ppapi_proxy', 440 'print', 441 'raf_investigation', 442 'rail', 443 'renderer', 444 'renderer_host', 445 'renderer.scheduler', 446 'resources', 447 'RLZ', 448 'ServiceWorker', 449 'SiteEngagement', 450 'safe_browsing', 451 'scheduler', 452 'scheduler.long_tasks', 453 'screenlock_monitor', 454 'segmentation_platform', 455 'sequence_manager', 456 'service_manager', 457 'sharing', 458 'shell', 459 'shortcut_viewer', 460 'shutdown', 461 'skia', 462 'sql', 463 'stadia_media', 464 'stadia_rtc', 465 'startup', 466 'sync', 467 'system_apps', 468 'test_gpu', 469 'toplevel', 470 'toplevel.flow', 471 'ui', 472 'v8', 473 'v8.execute', 474 'v8.wasm', 475 'ValueStoreFrontend::Backend', 476 'views', 477 'views.frame', 478 'viz', 479 'vk', 480 'wakeup.flow', 481 'wayland', 482 'webaudio', 483 'webengine.fidl', 484 'weblayer', 485 'WebCore', 486 'webnn', 487 'webrtc', 488 'webrtc_stats', 489 'xr', 490 'disabled-by-default-android_view_hierarchy', 491 'disabled-by-default-animation-worklet', 492 'disabled-by-default-audio', 493 'disabled-by-default-audio.latency', 494 'disabled-by-default-audio-worklet', 495 'disabled-by-default-base', 496 'disabled-by-default-blink.debug', 497 'disabled-by-default-blink.debug.display_lock', 498 'disabled-by-default-blink.debug.layout', 499 'disabled-by-default-blink.debug.layout.trees', 500 'disabled-by-default-blink.feature_usage', 501 'disabled-by-default-blink.image_decoding', 502 'disabled-by-default-blink.invalidation', 503 'disabled-by-default-identifiability', 504 'disabled-by-default-identifiability.high_entropy_api', 505 'disabled-by-default-cc', 506 'disabled-by-default-cc.debug', 507 'disabled-by-default-cc.debug.cdp-perf', 508 'disabled-by-default-cc.debug.display_items', 509 'disabled-by-default-cc.debug.lcd_text', 510 'disabled-by-default-cc.debug.picture', 511 'disabled-by-default-cc.debug.scheduler', 512 'disabled-by-default-cc.debug.scheduler.frames', 513 'disabled-by-default-cc.debug.scheduler.now', 514 'disabled-by-default-content.verbose', 515 'disabled-by-default-cpu_profiler', 516 'disabled-by-default-cppgc', 517 'disabled-by-default-cpu_profiler.debug', 518 'disabled-by-default-devtools.screenshot', 519 'disabled-by-default-devtools.timeline', 520 'disabled-by-default-devtools.timeline.frame', 521 'disabled-by-default-devtools.timeline.inputs', 522 'disabled-by-default-devtools.timeline.invalidationTracking', 523 'disabled-by-default-devtools.timeline.layers', 524 'disabled-by-default-devtools.timeline.picture', 525 'disabled-by-default-devtools.timeline.stack', 526 'disabled-by-default-devtools.target-rundown', 527 'disabled-by-default-devtools.v8-source-rundown', 528 'disabled-by-default-devtools.v8-source-rundown-sources', 529 'disabled-by-default-file', 530 'disabled-by-default-fonts', 531 'disabled-by-default-gpu_cmd_queue', 532 'disabled-by-default-gpu.dawn', 533 'disabled-by-default-gpu.debug', 534 'disabled-by-default-gpu.decoder', 535 'disabled-by-default-gpu.device', 536 'disabled-by-default-gpu.graphite.dawn', 537 'disabled-by-default-gpu.service', 538 'disabled-by-default-gpu.vulkan.vma', 539 'disabled-by-default-histogram_samples', 540 'disabled-by-default-java-heap-profiler', 541 'disabled-by-default-layer-element', 542 'disabled-by-default-layout_shift.debug', 543 'disabled-by-default-lifecycles', 544 'disabled-by-default-loading', 545 'disabled-by-default-mediastream', 546 'disabled-by-default-memory-infra', 547 'disabled-by-default-memory-infra.v8.code_stats', 548 'disabled-by-default-mojom', 549 'disabled-by-default-net', 550 'disabled-by-default-network', 551 'disabled-by-default-paint-worklet', 552 'disabled-by-default-power', 553 'disabled-by-default-renderer.scheduler', 554 'disabled-by-default-renderer.scheduler.debug', 555 'disabled-by-default-sequence_manager', 556 'disabled-by-default-sequence_manager.debug', 557 'disabled-by-default-sequence_manager.verbose_snapshots', 558 'disabled-by-default-skia', 559 'disabled-by-default-skia.gpu', 560 'disabled-by-default-skia.gpu.cache', 561 'disabled-by-default-skia.shaders', 562 'disabled-by-default-skottie', 563 'disabled-by-default-SyncFileSystem', 564 'disabled-by-default-system_power', 565 'disabled-by-default-system_stats', 566 'disabled-by-default-thread_pool_diagnostics', 567 'disabled-by-default-toplevel.ipc', 568 'disabled-by-default-user_action_samples', 569 'disabled-by-default-v8.compile', 570 'disabled-by-default-v8.cpu_profiler', 571 'disabled-by-default-v8.gc', 572 'disabled-by-default-v8.gc_stats', 573 'disabled-by-default-v8.ic_stats', 574 'disabled-by-default-v8.inspector', 575 'disabled-by-default-v8.runtime', 576 'disabled-by-default-v8.runtime_stats', 577 'disabled-by-default-v8.runtime_stats_sampling', 578 'disabled-by-default-v8.stack_trace', 579 'disabled-by-default-v8.turbofan', 580 'disabled-by-default-v8.wasm.detailed', 581 'disabled-by-default-v8.wasm.turbofan', 582 'disabled-by-default-video_and_image_capture', 583 'disabled-by-default-display.framedisplayed', 584 'disabled-by-default-viz.gpu_composite_time', 585 'disabled-by-default-viz.debug.overlay_planes', 586 'disabled-by-default-viz.hit_testing_flow', 587 'disabled-by-default-viz.overdraw', 588 'disabled-by-default-viz.quads', 589 'disabled-by-default-viz.surface_id_flow', 590 'disabled-by-default-viz.surface_lifetime', 591 'disabled-by-default-viz.triangles', 592 'disabled-by-default-viz.visual_debugger', 593 'disabled-by-default-webaudio.audionode', 594 'disabled-by-default-webgpu', 595 'disabled-by-default-webnn', 596 'disabled-by-default-webrtc', 597 'disabled-by-default-worker.scheduler', 598 'disabled-by-default-xr.debug', 599]; 600