• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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