Add Perfetto protolog parser Test: npm run test:presubmit Bug: 276432490 Change-Id: I20e753e926f1bb6741c8bb7b6c3696ebd46b44db
diff --git a/tools/winscope/src/parsers/perfetto/parser_factory.ts b/tools/winscope/src/parsers/perfetto/parser_factory.ts index b208996..c10b2eb 100644 --- a/tools/winscope/src/parsers/perfetto/parser_factory.ts +++ b/tools/winscope/src/parsers/perfetto/parser_factory.ts
@@ -20,12 +20,18 @@ import {Parser} from 'trace/parser'; import {TraceFile} from 'trace/trace_file'; import {initWasm, resetEngineWorker, WasmEngineProxy} from 'trace_processor/wasm_engine_proxy'; +import {ParserProtolog} from './parser_protolog'; import {ParserTransactions} from './parser_transactions'; import {ParserTransitions} from './parser_transitions'; import {ParserSurfaceFlinger} from './surface_flinger/parser_surface_flinger'; export class ParserFactory { - private static readonly PARSERS = [ParserSurfaceFlinger, ParserTransactions, ParserTransitions]; + private static readonly PARSERS = [ + ParserSurfaceFlinger, + ParserTransactions, + ParserTransitions, + ParserProtolog, + ]; private static readonly CHUNK_SIZE_BYTES = 50 * 1024 * 1024; private static traceProcessor?: WasmEngineProxy;
diff --git a/tools/winscope/src/parsers/perfetto/parser_protolog.ts b/tools/winscope/src/parsers/perfetto/parser_protolog.ts new file mode 100644 index 0000000..6362166 --- /dev/null +++ b/tools/winscope/src/parsers/perfetto/parser_protolog.ts
@@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 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 {assertDefined} from 'common/assert_utils'; +import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time'; +import {TimeUtils} from 'common/time_utils'; +import {LogMessage} from 'trace/protolog'; +import {TraceFile} from 'trace/trace_file'; +import {TraceType} from 'trace/trace_type'; +import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy'; +import {AbstractParser} from './abstract_parser'; + +class PerfettoLogMessageTableRow { + message: string = '<NO_MESSAGE>'; + tag: string = '<NO_TAG>'; + level: string = '<NO_LEVEL>'; + location: string = '<NO_LOC>'; + timestamp: bigint = 0n; + + constructor(timestamp: bigint, tag: string, level: string, message: string) { + this.timestamp = timestamp ?? this.timestamp; + this.tag = tag ?? this.tag; + this.level = level ?? this.level; + this.message = message ?? this.message; + } +} + +export class ParserProtolog extends AbstractParser<LogMessage> { + constructor(traceFile: TraceFile, traceProcessor: WasmEngineProxy) { + super(traceFile, traceProcessor); + } + + override getTraceType(): TraceType { + return TraceType.PROTO_LOG; + } + + override async getEntry(index: number, timestampType: TimestampType): Promise<LogMessage> { + const protologEntry = await this.queryProtoLogEntry(index); + + let time: string; + let timestamp: bigint; + const realToElapsedTimeOffsetNs = assertDefined(this.realToElapsedTimeOffsetNs); + if (timestampType === TimestampType.REAL) { + timestamp = protologEntry.timestamp + realToElapsedTimeOffsetNs; + time = TimeUtils.format(new RealTimestamp(timestamp)); + } else { + timestamp = protologEntry.timestamp; + time = TimeUtils.format(new ElapsedTimestamp(timestamp)); + } + + return new LogMessage( + protologEntry.message, + time, + protologEntry.tag, + protologEntry.level, + protologEntry.location, + timestamp + ); + } + + protected override getTableName(): string { + return 'protolog'; + } + + private async queryProtoLogEntry(index: number): Promise<PerfettoLogMessageTableRow> { + const sql = ` + SELECT + ts, tag, level, message + FROM + protolog + WHERE protolog.id = ${index}; + `; + const result = await this.traceProcessor.query(sql).waitAllRows(); + + if (result.numRows() !== 1) { + throw new Error( + `Expected exactly 1 protolog message with id ${index} but got ${result.numRows()}` + ); + } + + const entry = result.iter({}); + + return new PerfettoLogMessageTableRow( + entry.get('ts') as bigint, + entry.get('tag') as string, + entry.get('level') as string, + entry.get('message') as string + ); + } +}
diff --git a/tools/winscope/src/parsers/perfetto/parser_protolog_test.ts b/tools/winscope/src/parsers/perfetto/parser_protolog_test.ts new file mode 100644 index 0000000..d734f1f --- /dev/null +++ b/tools/winscope/src/parsers/perfetto/parser_protolog_test.ts
@@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 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 {assertDefined} from 'common/assert_utils'; +import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'common/time'; +import {UnitTestUtils} from 'test/unit/utils'; +import {Parser} from 'trace/parser'; +import {LogMessage} from 'trace/protolog'; +import {TraceType} from 'trace/trace_type'; + +describe('Perfetto ParserProtolog', () => { + let parser: Parser<object>; + + const expectedFirstLogMessageElapsed = { + text: 'Sent Transition (#11) createdAt=01-29 17:54:23.793', + time: '1h38m59s2ms349294ns', + tag: 'WindowManager', + level: 'VERBOSE', + at: '<NO_LOC>', + timestamp: 5939002349294n, + }; + + const expectedFirstLogMessageReal = { + text: 'Sent Transition (#11) createdAt=01-29 17:54:23.793', + time: '2024-01-29T16:54:24.827624563', + tag: 'WindowManager', + level: 'VERBOSE', + at: '<NO_LOC>', + timestamp: 1706547264827624563n, + }; + + beforeAll(async () => { + parser = await UnitTestUtils.getPerfettoParser( + TraceType.PROTO_LOG, + 'traces/perfetto/protolog.perfetto-trace' + ); + }); + + it('has expected trace type', () => { + expect(parser.getTraceType()).toEqual(TraceType.PROTO_LOG); + }); + + it('provides elapsed timestamps', () => { + const timestamps = assertDefined(parser.getTimestamps(TimestampType.ELAPSED)); + + expect(timestamps.length).toEqual(75); + + // TODO: They shouldn't all have the same timestamp... + const expected = [ + new ElapsedTimestamp(5939002349294n), + new ElapsedTimestamp(5939002349294n), + new ElapsedTimestamp(5939002349294n), + ]; + expect(timestamps.slice(0, 3)).toEqual(expected); + }); + + it('provides real timestamps', () => { + const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL)); + + expect(timestamps.length).toEqual(75); + + // TODO: They shouldn't all have the same timestamp... + const expected = [ + new RealTimestamp(1706547264827624563n), + new RealTimestamp(1706547264827624563n), + new RealTimestamp(1706547264827624563n), + ]; + expect(timestamps.slice(0, 3)).toEqual(expected); + }); + + it('reconstructs human-readable log message (ELAPSED time)', async () => { + const message = await parser.getEntry(0, TimestampType.ELAPSED); + + expect(Object.assign({}, message)).toEqual(expectedFirstLogMessageElapsed); + expect(message).toBeInstanceOf(LogMessage); + }); + + it('reconstructs human-readable log message (REAL time)', async () => { + const message = await parser.getEntry(0, TimestampType.REAL); + + expect(Object.assign({}, message)).toEqual(expectedFirstLogMessageReal); + expect(message).toBeInstanceOf(LogMessage); + }); +});
diff --git a/tools/winscope/src/test/fixtures/traces/perfetto/protolog.perfetto-trace b/tools/winscope/src/test/fixtures/traces/perfetto/protolog.perfetto-trace new file mode 100644 index 0000000..48e0296 --- /dev/null +++ b/tools/winscope/src/test/fixtures/traces/perfetto/protolog.perfetto-trace Binary files differ