1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {PersistentStoreProxy} from 'common/store/persistent_store_proxy'; 18import {Store} from 'common/store/store'; 19import {TRACE_INFO} from 'trace/trace_info'; 20import {TraceType} from 'trace/trace_type'; 21import {UiTraceTarget} from './ui_trace_target'; 22 23/** 24 * Represents a trace target and its required/optional configuration. 25 */ 26export interface TraceConfiguration { 27 name: string; 28 config: ConfigurationOptions; 29 available: boolean; 30 types: TraceType[]; 31} 32 33/** 34 * Maps trace targets to their configuration. 35 */ 36export interface TraceConfigurationMap { 37 [key: string]: TraceConfiguration; 38} 39 40/** 41 * Contains all config options for a trace target that should be stored 42 * between sessions 43 * @prop {boolean} enabled indicates whether a trace target should be run 44 * @prop {CheckboxConfiguration} checkboxConfigs List of configs that can be optionally enabled 45 * @prop {SelectionConfiguration} selectionConfigs List of configs whose values are set from a list of options 46 */ 47export interface ConfigurationOptions { 48 enabled: boolean; 49 checkboxConfigs: CheckboxConfiguration[]; 50 selectionConfigs: SelectionConfiguration[]; 51} 52 53interface AdvancedConfiguration { 54 name: string; 55 key: string; 56} 57 58export interface CheckboxConfiguration extends AdvancedConfiguration { 59 enabled: boolean; 60} 61 62export interface SelectionConfiguration extends AdvancedConfiguration { 63 options: string[]; 64 value: string | string[]; 65 desc?: string; 66 optional?: boolean; 67 wideField?: boolean; 68} 69 70export interface ConfigMap { 71 [key: string]: string[] | string; 72} 73 74const wmTraceSelectionConfigs: SelectionConfiguration[] = [ 75 { 76 key: 'wmbuffersize', 77 name: 'buffer size (KB)', 78 options: ['4000', '8000', '16000', '32000'], 79 value: '32000', 80 }, 81 { 82 key: 'tracingtype', 83 name: 'tracing type', 84 options: ['frame', 'transaction'], 85 value: 'frame', 86 }, 87 { 88 key: 'tracinglevel', 89 name: 'tracing level', 90 options: ['verbose', 'debug', 'critical'], 91 value: 'verbose', 92 }, 93]; 94 95const sfTraceCheckboxConfigs: CheckboxConfiguration[] = [ 96 { 97 name: 'input', 98 key: 'input', 99 enabled: true, 100 }, 101 { 102 name: 'composition', 103 key: 'composition', 104 enabled: true, 105 }, 106 { 107 name: 'metadata', 108 key: 'metadata', 109 enabled: false, 110 }, 111 { 112 name: 'hwc', 113 key: 'hwc', 114 enabled: false, 115 }, 116 { 117 name: 'trace buffers', 118 key: 'tracebuffers', 119 enabled: false, 120 }, 121 { 122 name: 'virtual displays', 123 key: 'virtualdisplays', 124 enabled: false, 125 }, 126]; 127 128const sfTraceSelectionConfigs: SelectionConfiguration[] = [ 129 { 130 key: 'sfbuffersize', 131 name: 'buffer size (KB)', 132 options: ['4000', '8000', '16000', '32000'], 133 value: '32000', 134 }, 135]; 136const screenshotConfigs: SelectionConfiguration[] = [ 137 { 138 key: 'displays', 139 name: 'displays', 140 options: [], 141 value: [], 142 desc: 'Leave empty to capture active display', 143 wideField: true, 144 }, 145]; 146 147export function makeScreenRecordingSelectionConfigs( 148 options: string[], 149 initialValue: string | string[], 150): SelectionConfiguration[] { 151 return [ 152 { 153 key: 'displays', 154 name: 'displays', 155 options, 156 value: initialValue, 157 optional: true, 158 desc: 'Leave empty to capture active display', 159 wideField: true, 160 }, 161 ]; 162} 163 164const traceDefaultConfig = new Map([ 165 [ 166 UiTraceTarget.SURFACE_FLINGER_TRACE, 167 { 168 name: TRACE_INFO[TraceType.SURFACE_FLINGER].name, 169 config: { 170 enabled: true, 171 checkboxConfigs: sfTraceCheckboxConfigs, 172 selectionConfigs: sfTraceSelectionConfigs, 173 }, 174 available: true, 175 types: [TraceType.SURFACE_FLINGER], 176 }, 177 ], 178 [ 179 UiTraceTarget.WINDOW_MANAGER_TRACE, 180 { 181 name: TRACE_INFO[TraceType.WINDOW_MANAGER].name, 182 config: { 183 enabled: true, 184 checkboxConfigs: [], 185 selectionConfigs: wmTraceSelectionConfigs, 186 }, 187 available: true, 188 types: [TraceType.WINDOW_MANAGER], 189 }, 190 ], 191 [ 192 UiTraceTarget.SCREEN_RECORDING, 193 { 194 name: TRACE_INFO[TraceType.SCREEN_RECORDING].name, 195 config: { 196 enabled: true, 197 checkboxConfigs: [ 198 { 199 name: 'pointer location and touches', 200 key: 'pointer_and_touches', 201 enabled: true, 202 }, 203 ], 204 selectionConfigs: makeScreenRecordingSelectionConfigs([], ''), 205 }, 206 available: true, 207 types: [TraceType.SCREEN_RECORDING], 208 }, 209 ], 210 [ 211 UiTraceTarget.IME, 212 { 213 name: 'IME', 214 config: { 215 enabled: true, 216 checkboxConfigs: [], 217 selectionConfigs: [], 218 }, 219 available: true, 220 types: [ 221 TraceType.INPUT_METHOD_CLIENTS, 222 TraceType.INPUT_METHOD_SERVICE, 223 TraceType.INPUT_METHOD_MANAGER_SERVICE, 224 ], 225 }, 226 ], 227 [ 228 UiTraceTarget.TRANSACTIONS, 229 { 230 name: TRACE_INFO[TraceType.TRANSACTIONS].name, 231 config: { 232 enabled: true, 233 checkboxConfigs: [], 234 selectionConfigs: [], 235 }, 236 available: true, 237 types: [TraceType.TRANSACTIONS], 238 }, 239 ], 240 [ 241 UiTraceTarget.PROTO_LOG, 242 { 243 name: TRACE_INFO[TraceType.PROTO_LOG].name, 244 config: { 245 enabled: false, 246 checkboxConfigs: [], 247 selectionConfigs: [], 248 }, 249 available: true, 250 types: [TraceType.PROTO_LOG], 251 }, 252 ], 253 [ 254 UiTraceTarget.WAYLAND, 255 { 256 name: TRACE_INFO[TraceType.WAYLAND].name, 257 config: { 258 enabled: false, 259 checkboxConfigs: [], 260 selectionConfigs: [], 261 }, 262 available: false, 263 types: [TraceType.WAYLAND, TraceType.WAYLAND_DUMP], 264 }, 265 ], 266 [ 267 UiTraceTarget.EVENTLOG, 268 { 269 name: TRACE_INFO[TraceType.EVENT_LOG].name + ' (CUJs)', 270 config: { 271 enabled: false, 272 checkboxConfigs: [], 273 selectionConfigs: [], 274 }, 275 available: true, 276 types: [TraceType.EVENT_LOG, TraceType.CUJS], 277 }, 278 ], 279 [ 280 UiTraceTarget.TRANSITIONS, 281 { 282 name: TRACE_INFO[TraceType.SHELL_TRANSITION].name, 283 config: { 284 enabled: false, 285 checkboxConfigs: [], 286 selectionConfigs: [], 287 }, 288 available: true, 289 types: [ 290 TraceType.SHELL_TRANSITION, 291 TraceType.WM_TRANSITION, 292 TraceType.TRANSITION, 293 ], 294 }, 295 ], 296 [ 297 UiTraceTarget.VIEW_CAPTURE, 298 { 299 name: TRACE_INFO[TraceType.VIEW_CAPTURE].name, 300 config: { 301 enabled: false, 302 checkboxConfigs: [], 303 selectionConfigs: [], 304 }, 305 available: true, 306 types: [TraceType.VIEW_CAPTURE], 307 }, 308 ], 309 [ 310 UiTraceTarget.INPUT, 311 { 312 name: 'Input', 313 config: { 314 enabled: true, 315 checkboxConfigs: [], 316 selectionConfigs: [], 317 }, 318 available: true, 319 types: [ 320 TraceType.INPUT_KEY_EVENT, 321 TraceType.INPUT_MOTION_EVENT, 322 TraceType.INPUT_EVENT_MERGED, 323 ], 324 }, 325 ], 326]); 327 328export function makeDefaultTraceConfigMap(): TraceConfigurationMap { 329 const map = new Map<UiTraceTarget, TraceConfiguration>(); 330 for (const [target, config] of traceDefaultConfig.entries()) { 331 map.set(target, structuredClone(config)); 332 } 333 return Object.fromEntries(map); 334} 335 336const dumpDefaultConfig = new Map([ 337 [ 338 UiTraceTarget.WINDOW_MANAGER_DUMP, 339 { 340 name: 'Window Manager', 341 config: { 342 enabled: true, 343 checkboxConfigs: [], 344 selectionConfigs: [], 345 }, 346 available: true, 347 types: [TraceType.WINDOW_MANAGER], 348 }, 349 ], 350 [ 351 UiTraceTarget.SURFACE_FLINGER_DUMP, 352 { 353 name: 'Surface Flinger', 354 config: { 355 enabled: true, 356 checkboxConfigs: [], 357 selectionConfigs: [], 358 }, 359 available: true, 360 types: [TraceType.SURFACE_FLINGER], 361 }, 362 ], 363 [ 364 UiTraceTarget.SCREENSHOT, 365 { 366 name: 'Screenshot', 367 config: { 368 enabled: true, 369 checkboxConfigs: [], 370 selectionConfigs: screenshotConfigs, 371 }, 372 available: true, 373 types: [TraceType.SCREENSHOT], 374 }, 375 ], 376]); 377 378export function makeDefaultDumpConfigMap(): TraceConfigurationMap { 379 const map = new Map<UiTraceTarget, TraceConfiguration>(); 380 for (const [target, config] of dumpDefaultConfig.entries()) { 381 map.set(target, structuredClone(config)); 382 } 383 return Object.fromEntries(map); 384} 385 386/** 387 * Updates configurations for each trace target from store. 388 * Stored configs are used only to update available configs, not to add or remove configs. 389 * @param configMap contains available configs with default values set 390 * @param store contains previous trace configurations - used to update values in configMap for configs that are still available 391 * @param storeKeyPrefix used with the trace target key in configMap to retrieve configs from the store 392 */ 393export function updateConfigsFromStore( 394 configMap: TraceConfigurationMap, 395 storage: Store, 396 storeKeyPrefix: string, 397) { 398 for (const [key, target] of Object.entries(configMap)) { 399 const stored = PersistentStoreProxy.new( 400 storeKeyPrefix + key, 401 target.config, 402 storage, 403 ); 404 stored.checkboxConfigs = mergeConfigs( 405 target.config.checkboxConfigs, 406 stored.checkboxConfigs, 407 (liveConfig, storedConfig) => { 408 liveConfig.enabled = storedConfig.enabled; 409 }, 410 ); 411 stored.selectionConfigs = mergeConfigs( 412 target.config.selectionConfigs, 413 stored.selectionConfigs, 414 (liveConfig, storedConfig) => { 415 if (typeof liveConfig.value !== typeof storedConfig.value) { 416 // config schema has changed between single and multiple selection 417 // so stored value no longer applies 418 return; 419 } 420 421 // liveConfig contains all currently available config options - stored 422 // values are valid if their config option is still available, and only 423 // if valid should be used to update the values in liveConfig 424 const availableOptions = liveConfig.options; 425 const validArr = 426 Array.isArray(storedConfig.value) && 427 storedConfig.value.every((v) => availableOptions.includes(v)); 428 const validStr = 429 typeof storedConfig.value === 'string' && 430 availableOptions.includes(storedConfig.value); 431 if (validArr || validStr) { 432 liveConfig.value = storedConfig.value; 433 } 434 }, 435 ); 436 target.config = stored; 437 } 438 return configMap; 439} 440 441function mergeConfigs<T extends AdvancedConfiguration>( 442 liveConfigs: T[], 443 storedConfigs: T[], 444 updateConfig: (liveConfig: T, storedConfig: T) => void, 445): T[] { 446 storedConfigs.forEach((storedConfig) => { 447 const liveConfig = liveConfigs.find( 448 (live) => live.key === storedConfig.key, 449 ); 450 if (liveConfig) { 451 updateConfig(liveConfig, storedConfig); 452 } 453 }); 454 return liveConfigs; 455} 456