1/* 2 * Copyright (C) 2023 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 {globalConfig} from 'common/global_config'; 18import {ParserTimestampConverter} from 'common/timestamp_converter'; 19import {UrlUtils} from 'common/url_utils'; 20import {ProgressListener} from 'messaging/progress_listener'; 21import {UserNotificationsListener} from 'messaging/user_notifications_listener'; 22import {InvalidPerfettoTrace} from 'messaging/user_warnings'; 23import {ParserKeyEvent} from 'parsers/input/perfetto/parser_key_event'; 24import {ParserMotionEvent} from 'parsers/input/perfetto/parser_motion_event'; 25import {ParserInputMethodClients} from 'parsers/input_method/perfetto/parser_input_method_clients'; 26import {ParserInputMethodManagerService} from 'parsers/input_method/perfetto/parser_input_method_manager_service'; 27import {ParserInputMethodService} from 'parsers/input_method/perfetto/parser_input_method_service'; 28import {ParserProtolog} from 'parsers/protolog/perfetto/parser_protolog'; 29import {ParserSurfaceFlinger} from 'parsers/surface_flinger/perfetto/parser_surface_flinger'; 30import {ParserTransactions} from 'parsers/transactions/perfetto/parser_transactions'; 31import {ParserTransitions} from 'parsers/transitions/perfetto/parser_transitions'; 32import {ParserViewCapture} from 'parsers/view_capture/perfetto/parser_view_capture'; 33import {Parser} from 'trace/parser'; 34import {TraceFile} from 'trace/trace_file'; 35import { 36 initWasm, 37 resetEngineWorker, 38 WasmEngineProxy, 39} from 'trace_processor/wasm_engine_proxy'; 40 41export class ParserFactory { 42 private static readonly PARSERS = [ 43 ParserInputMethodClients, 44 ParserInputMethodManagerService, 45 ParserInputMethodService, 46 ParserProtolog, 47 ParserSurfaceFlinger, 48 ParserTransactions, 49 ParserTransitions, 50 ParserViewCapture, 51 ParserMotionEvent, 52 ParserKeyEvent, 53 ]; 54 private static readonly CHUNK_SIZE_BYTES = 50 * 1024 * 1024; 55 private static traceProcessor?: WasmEngineProxy; 56 57 async createParsers( 58 traceFile: TraceFile, 59 timestampConverter: ParserTimestampConverter, 60 progressListener?: ProgressListener, 61 notificationListener?: UserNotificationsListener, 62 ): Promise<Array<Parser<object>>> { 63 const traceProcessor = await this.initializeTraceProcessor(); 64 for ( 65 let chunkStart = 0; 66 chunkStart < traceFile.file.size; 67 chunkStart += ParserFactory.CHUNK_SIZE_BYTES 68 ) { 69 progressListener?.onProgressUpdate( 70 'Loading perfetto trace...', 71 (chunkStart / traceFile.file.size) * 100, 72 ); 73 const chunkEnd = chunkStart + ParserFactory.CHUNK_SIZE_BYTES; 74 const data = await traceFile.file 75 .slice(chunkStart, chunkEnd) 76 .arrayBuffer(); 77 try { 78 await traceProcessor.parse(new Uint8Array(data)); 79 } catch (e) { 80 console.error('Trace processor failed to parse data:', e); 81 return []; 82 } 83 } 84 await traceProcessor.notifyEof(); 85 86 progressListener?.onProgressUpdate( 87 'Reading from trace processor...', 88 undefined, 89 ); 90 const parsers: Array<Parser<object>> = []; 91 92 let hasFoundParser = false; 93 94 const errors: string[] = []; 95 for (const ParserType of ParserFactory.PARSERS) { 96 try { 97 const parser = new ParserType( 98 traceFile, 99 traceProcessor, 100 timestampConverter, 101 ); 102 await parser.parse(); 103 if (parser instanceof ParserViewCapture) { 104 parsers.push(...parser.getWindowParsers()); 105 } else { 106 parsers.push(parser); 107 } 108 hasFoundParser = true; 109 } catch (error) { 110 // skip current parser 111 errors.push((error as Error).message); 112 } 113 } 114 115 if (!hasFoundParser) { 116 notificationListener?.onNotifications([ 117 new InvalidPerfettoTrace(traceFile.getDescriptor(), errors), 118 ]); 119 } 120 121 return parsers; 122 } 123 124 private async initializeTraceProcessor(): Promise<WasmEngineProxy> { 125 if (!ParserFactory.traceProcessor) { 126 const traceProcessorRootUrl = 127 globalConfig.MODE === 'KARMA_TEST' 128 ? UrlUtils.getRootUrl() + 129 'base/deps_build/trace_processor/to_be_served/' 130 : UrlUtils.getRootUrl(); 131 initWasm(traceProcessorRootUrl); 132 const engineId = 'random-id'; 133 const enginePort = resetEngineWorker(); 134 ParserFactory.traceProcessor = new WasmEngineProxy(engineId, enginePort); 135 } 136 137 await ParserFactory.traceProcessor.resetTraceProcessor({ 138 cropTrackEvents: false, 139 ingestFtraceInRawTable: false, 140 analyzeTraceProtoContent: false, 141 }); 142 143 return ParserFactory.traceProcessor; 144 } 145} 146