/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import {globalConfig} from 'common/global_config'; import {ParserTimestampConverter} from 'common/timestamp_converter'; import {UrlUtils} from 'common/url_utils'; import {ProgressListener} from 'messaging/progress_listener'; import {UserNotificationsListener} from 'messaging/user_notifications_listener'; import {InvalidPerfettoTrace} from 'messaging/user_warnings'; import {ParserKeyEvent} from 'parsers/input/perfetto/parser_key_event'; import {ParserMotionEvent} from 'parsers/input/perfetto/parser_motion_event'; import {ParserInputMethodClients} from 'parsers/input_method/perfetto/parser_input_method_clients'; import {ParserInputMethodManagerService} from 'parsers/input_method/perfetto/parser_input_method_manager_service'; import {ParserInputMethodService} from 'parsers/input_method/perfetto/parser_input_method_service'; import {ParserProtolog} from 'parsers/protolog/perfetto/parser_protolog'; import {ParserSurfaceFlinger} from 'parsers/surface_flinger/perfetto/parser_surface_flinger'; import {ParserTransactions} from 'parsers/transactions/perfetto/parser_transactions'; import {ParserTransitions} from 'parsers/transitions/perfetto/parser_transitions'; import {ParserViewCapture} from 'parsers/view_capture/perfetto/parser_view_capture'; import {Parser} from 'trace/parser'; import {TraceFile} from 'trace/trace_file'; import { initWasm, resetEngineWorker, WasmEngineProxy, } from 'trace_processor/wasm_engine_proxy'; export class ParserFactory { private static readonly PARSERS = [ ParserInputMethodClients, ParserInputMethodManagerService, ParserInputMethodService, ParserProtolog, ParserSurfaceFlinger, ParserTransactions, ParserTransitions, ParserViewCapture, ParserMotionEvent, ParserKeyEvent, ]; private static readonly CHUNK_SIZE_BYTES = 50 * 1024 * 1024; private static traceProcessor?: WasmEngineProxy; async createParsers( traceFile: TraceFile, timestampConverter: ParserTimestampConverter, progressListener?: ProgressListener, notificationListener?: UserNotificationsListener, ): Promise>> { const traceProcessor = await this.initializeTraceProcessor(); for ( let chunkStart = 0; chunkStart < traceFile.file.size; chunkStart += ParserFactory.CHUNK_SIZE_BYTES ) { progressListener?.onProgressUpdate( 'Loading perfetto trace...', (chunkStart / traceFile.file.size) * 100, ); const chunkEnd = chunkStart + ParserFactory.CHUNK_SIZE_BYTES; const data = await traceFile.file .slice(chunkStart, chunkEnd) .arrayBuffer(); try { await traceProcessor.parse(new Uint8Array(data)); } catch (e) { console.error('Trace processor failed to parse data:', e); return []; } } await traceProcessor.notifyEof(); progressListener?.onProgressUpdate( 'Reading from trace processor...', undefined, ); const parsers: Array> = []; let hasFoundParser = false; const errors: string[] = []; for (const ParserType of ParserFactory.PARSERS) { try { const parser = new ParserType( traceFile, traceProcessor, timestampConverter, ); await parser.parse(); if (parser instanceof ParserViewCapture) { parsers.push(...parser.getWindowParsers()); } else { parsers.push(parser); } hasFoundParser = true; } catch (error) { // skip current parser errors.push((error as Error).message); } } if (!hasFoundParser) { notificationListener?.onNotifications([ new InvalidPerfettoTrace(traceFile.getDescriptor(), errors), ]); } return parsers; } private async initializeTraceProcessor(): Promise { if (!ParserFactory.traceProcessor) { const traceProcessorRootUrl = globalConfig.MODE === 'KARMA_TEST' ? UrlUtils.getRootUrl() + 'base/deps_build/trace_processor/to_be_served/' : UrlUtils.getRootUrl(); initWasm(traceProcessorRootUrl); const engineId = 'random-id'; const enginePort = resetEngineWorker(); ParserFactory.traceProcessor = new WasmEngineProxy(engineId, enginePort); } await ParserFactory.traceProcessor.resetTraceProcessor({ cropTrackEvents: false, ingestFtraceInRawTable: false, analyzeTraceProtoContent: false, }); return ParserFactory.traceProcessor; } }