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 {ParserTimestampConverter} from 'common/time/timestamp_converter'; 18import {UserNotifier} from 'common/user_notifier'; 19import {Analytics} from 'logging/analytics'; 20import {ProgressListener} from 'messaging/progress_listener'; 21import { 22 InvalidPerfettoTrace, 23 PerfettoPacketLoss, 24} from 'messaging/user_warnings'; 25import {ParserKeyEvent} from 'parsers/input/perfetto/parser_key_event'; 26import {ParserMotionEvent} from 'parsers/input/perfetto/parser_motion_event'; 27import {ParserInputMethodClients} from 'parsers/input_method/perfetto/parser_input_method_clients'; 28import {ParserInputMethodManagerService} from 'parsers/input_method/perfetto/parser_input_method_manager_service'; 29import {ParserInputMethodService} from 'parsers/input_method/perfetto/parser_input_method_service'; 30import {ParserProtolog} from 'parsers/protolog/perfetto/parser_protolog'; 31import {ParserSurfaceFlinger} from 'parsers/surface_flinger/perfetto/parser_surface_flinger'; 32import {ParserTransactions} from 'parsers/transactions/perfetto/parser_transactions'; 33import {ParserTransitions} from 'parsers/transitions/perfetto/parser_transitions'; 34import {ParserViewCapture} from 'parsers/view_capture/perfetto/parser_view_capture'; 35import {ParserWindowManager} from 'parsers/window_manager/perfetto/parser_window_manager'; 36import {Parser} from 'trace/parser'; 37import {TraceFile} from 'trace/trace_file'; 38import {Row} from 'trace_processor/query_result'; 39import {TraceProcessor} from 'trace_processor/trace_processor'; 40import {TraceProcessorFactory} from 'trace_processor/trace_processor_factory'; 41 42export class ParserFactory { 43 private static readonly PARSERS = [ 44 ParserInputMethodClients, 45 ParserInputMethodManagerService, 46 ParserInputMethodService, 47 ParserProtolog, 48 ParserSurfaceFlinger, 49 ParserTransactions, 50 ParserTransitions, 51 ParserViewCapture, 52 ParserWindowManager, 53 ParserMotionEvent, 54 ParserKeyEvent, 55 ]; 56 private static readonly CHUNK_SIZE_BYTES = 50 * 1024 * 1024; 57 private static readonly NO_ENTRIES_ERROR_REGEX = 58 /Perfetto trace has no \w+(\w|\s)* entries/; 59 60 async createParsers( 61 traceFile: TraceFile, 62 timestampConverter: ParserTimestampConverter, 63 progressListener?: ProgressListener, 64 ): Promise<Array<Parser<object>>> { 65 const traceProcessor = await this.initializeTraceProcessor(); 66 for ( 67 let chunkStart = 0; 68 chunkStart < traceFile.file.size; 69 chunkStart += ParserFactory.CHUNK_SIZE_BYTES 70 ) { 71 progressListener?.onProgressUpdate( 72 'Loading perfetto trace...', 73 (chunkStart / traceFile.file.size) * 100, 74 ); 75 const chunkEnd = chunkStart + ParserFactory.CHUNK_SIZE_BYTES; 76 const data = await traceFile.file 77 .slice(chunkStart, chunkEnd) 78 .arrayBuffer(); 79 try { 80 await traceProcessor.parse(new Uint8Array(data)); 81 } catch (e) { 82 console.error('Trace processor failed to parse data:', e); 83 return []; 84 } 85 } 86 await traceProcessor.notifyEof(); 87 88 progressListener?.onProgressUpdate( 89 'Reading from trace processor...', 90 undefined, 91 ); 92 const parsers: Array<Parser<object>> = []; 93 94 let hasFoundParser = false; 95 96 const errors: string[] = []; 97 for (const ParserType of ParserFactory.PARSERS) { 98 try { 99 const parser = new ParserType( 100 traceFile, 101 traceProcessor, 102 timestampConverter, 103 ); 104 await parser.parse(); 105 if (parser instanceof ParserViewCapture) { 106 parsers.push(...parser.getWindowParsers()); 107 } else { 108 parsers.push(parser); 109 } 110 hasFoundParser = true; 111 } catch (error) { 112 // skip current parser 113 const msg = (error as Error).message; 114 if (!ParserFactory.NO_ENTRIES_ERROR_REGEX.test(msg)) { 115 // If TP contains no entries for a particular trace type, the resulting 116 // error message matches ParserFactory.NO_ENTRIES_ERROR_REGEX. These 117 // messages are discarded, and if no parser is found, one representative 118 // message is reported to the user below. 119 errors.push(msg); 120 } 121 } 122 } 123 124 if (!hasFoundParser) { 125 if (errors.length === 0) { 126 errors.push('Perfetto trace has no Winscope trace entries'); 127 } 128 UserNotifier.add( 129 new InvalidPerfettoTrace(traceFile.getDescriptor(), errors), 130 ); 131 } 132 const result = await traceProcessor.queryAllRows( 133 "select name, value from stats where name = 'traced_buf_trace_writer_packet_loss'", 134 ); 135 if (result.numRows() > 0) { 136 const value = result.firstRow<Row>({})['value']; 137 if (typeof value === 'bigint' && value > 0n) { 138 UserNotifier.add( 139 new PerfettoPacketLoss(traceFile.getDescriptor(), Number(value)), 140 ); 141 } 142 } 143 144 return parsers; 145 } 146 147 private async initializeTraceProcessor(): Promise<TraceProcessor> { 148 const traceProcessor = await TraceProcessorFactory.getSingleInstance(); 149 150 await traceProcessor.resetTraceProcessor({ 151 cropTrackEvents: false, 152 ingestFtraceInRawTable: false, 153 analyzeTraceProtoContent: false, 154 }); 155 Analytics.Memory.logUsage('tp_initialized'); 156 157 return traceProcessor; 158 } 159} 160