Add WM & Shell side transitions parsers
Bug: 277181336 Test: npm run test:all Change-Id: I7eb981367191c47d3b50b31ee66f147ff1d6943d
This commit is contained in:
@@ -125,8 +125,13 @@ export const TRACE_INFO: TraceInfoMap = {
|
|||||||
icon: EVENT_LOG_ICON,
|
icon: EVENT_LOG_ICON,
|
||||||
color: '#fdd663',
|
color: '#fdd663',
|
||||||
},
|
},
|
||||||
[TraceType.TRANSITION]: {
|
[TraceType.WM_TRANSITION]: {
|
||||||
name: 'Transition Trace',
|
name: 'WM Transitions',
|
||||||
|
icon: TRANSITION_ICON,
|
||||||
|
color: '#EC407A',
|
||||||
|
},
|
||||||
|
[TraceType.SHELL_TRANSITION]: {
|
||||||
|
name: 'Shell Transitions',
|
||||||
icon: TRANSITION_ICON,
|
icon: TRANSITION_ICON,
|
||||||
color: '#EC407A',
|
color: '#EC407A',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import {ParserScreenRecording} from './parser_screen_recording';
|
|||||||
import {ParserScreenRecordingLegacy} from './parser_screen_recording_legacy';
|
import {ParserScreenRecordingLegacy} from './parser_screen_recording_legacy';
|
||||||
import {ParserSurfaceFlinger} from './parser_surface_flinger';
|
import {ParserSurfaceFlinger} from './parser_surface_flinger';
|
||||||
import {ParserTransactions} from './parser_transactions';
|
import {ParserTransactions} from './parser_transactions';
|
||||||
import {ParserTransitions} from './parser_transitions';
|
import {ParserTransitionsShell} from './parser_transitions_shell';
|
||||||
|
import {ParserTransitionsWm} from './parser_transitions_wm';
|
||||||
import {ParserWindowManager} from './parser_window_manager';
|
import {ParserWindowManager} from './parser_window_manager';
|
||||||
import {ParserWindowManagerDump} from './parser_window_manager_dump';
|
import {ParserWindowManagerDump} from './parser_window_manager_dump';
|
||||||
|
|
||||||
@@ -46,7 +47,8 @@ export class ParserFactory {
|
|||||||
ParserWindowManager,
|
ParserWindowManager,
|
||||||
ParserWindowManagerDump,
|
ParserWindowManagerDump,
|
||||||
ParserEventLog,
|
ParserEventLog,
|
||||||
ParserTransitions,
|
ParserTransitionsWm,
|
||||||
|
ParserTransitionsShell,
|
||||||
];
|
];
|
||||||
|
|
||||||
private parsers = new Map<TraceType, Parser<object>>();
|
private parsers = new Map<TraceType, Parser<object>>();
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 {
|
|
||||||
CrossPlatform,
|
|
||||||
Transition,
|
|
||||||
TransitionChange,
|
|
||||||
TransitionType,
|
|
||||||
WindowingMode,
|
|
||||||
} from 'trace/flickerlib/common';
|
|
||||||
import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
|
|
||||||
import {TraceFile} from 'trace/trace_file';
|
|
||||||
import {TraceType} from 'trace/trace_type';
|
|
||||||
import {AbstractParser} from './abstract_parser';
|
|
||||||
import {TransitionsTraceFileProto} from './proto_types';
|
|
||||||
|
|
||||||
export class ParserTransitions extends AbstractParser {
|
|
||||||
constructor(trace: TraceFile) {
|
|
||||||
super(trace);
|
|
||||||
this.realToElapsedTimeOffsetNs = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
override getTraceType(): TraceType {
|
|
||||||
return TraceType.TRANSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
override getMagicNumber(): number[] {
|
|
||||||
return ParserTransitions.MAGIC_NUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
override decodeTrace(buffer: Uint8Array): any[] {
|
|
||||||
const decodedProto = TransitionsTraceFileProto.decode(buffer) as any;
|
|
||||||
this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
|
|
||||||
|
|
||||||
return decodedProto.transitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp {
|
|
||||||
if (type === TimestampType.ELAPSED) {
|
|
||||||
return new ElapsedTimestamp(BigInt(entryProto.createTimeNs));
|
|
||||||
}
|
|
||||||
if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
|
|
||||||
return new RealTimestamp(this.realToElapsedTimeOffsetNs + BigInt(entryProto.createTimeNs));
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
override processDecodedEntry(
|
|
||||||
index: number,
|
|
||||||
timestampType: TimestampType,
|
|
||||||
entryProto: any
|
|
||||||
): Transition {
|
|
||||||
if (!this.transitions) {
|
|
||||||
const transitions = this.decodedEntries.map((it) => this.parseProto(it));
|
|
||||||
this.transitions = transitions;
|
|
||||||
}
|
|
||||||
return this.transitions[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseProto(entryProto: any): Transition {
|
|
||||||
const changes = entryProto.targets.map((it: any) => {
|
|
||||||
const windowingMode = WindowingMode.WINDOWING_MODE_UNDEFINED; // TODO: Get the windowing mode
|
|
||||||
|
|
||||||
return new TransitionChange(
|
|
||||||
TransitionType.Companion.fromInt(it.mode),
|
|
||||||
it.layerId,
|
|
||||||
it.windowId,
|
|
||||||
windowingMode
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const createTime = CrossPlatform.timestamp.fromString(
|
|
||||||
entryProto.createTimeNs.toString(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const sendTime = CrossPlatform.timestamp.fromString(
|
|
||||||
entryProto.sendTimeNs.toString(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const finishTime = CrossPlatform.timestamp.fromString(
|
|
||||||
entryProto.finishTimeNs.toString(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const startTransactionId = entryProto.startTransactionId;
|
|
||||||
const finishTransactionId = entryProto.finishTransactionId;
|
|
||||||
const type = TransitionType.Companion.fromInt(entryProto.type);
|
|
||||||
const played = entryProto.finishTimeNs > 0;
|
|
||||||
const aborted = entryProto.sendTimeNs === 0;
|
|
||||||
|
|
||||||
return new Transition(
|
|
||||||
createTime,
|
|
||||||
sendTime,
|
|
||||||
finishTime,
|
|
||||||
startTransactionId,
|
|
||||||
finishTransactionId,
|
|
||||||
type,
|
|
||||||
changes,
|
|
||||||
played,
|
|
||||||
aborted
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private transitions: Transition[] | undefined;
|
|
||||||
private realToElapsedTimeOffsetNs: undefined | bigint;
|
|
||||||
private static readonly MAGIC_NUMBER = [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE
|
|
||||||
}
|
|
||||||
159
tools/winscope/src/parsers/parser_transitions_shell.ts
Normal file
159
tools/winscope/src/parsers/parser_transitions_shell.ts
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
CrossPlatform,
|
||||||
|
ShellTransitionData,
|
||||||
|
Transition,
|
||||||
|
WmTransitionData,
|
||||||
|
} from 'trace/flickerlib/common';
|
||||||
|
import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
|
||||||
|
import {TraceType} from 'trace/trace_type';
|
||||||
|
import {AbstractParser} from './abstract_parser';
|
||||||
|
import {ShellTransitionsTraceFileProto} from './proto_types';
|
||||||
|
|
||||||
|
export class ParserTransitionsShell extends AbstractParser {
|
||||||
|
private realToElapsedTimeOffsetNs: undefined | bigint;
|
||||||
|
private handlerMapping: undefined | Map<number, string>;
|
||||||
|
|
||||||
|
override getTraceType(): TraceType {
|
||||||
|
return TraceType.SHELL_TRANSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
override decodeTrace(traceBuffer: Uint8Array): Transition[] {
|
||||||
|
const decodedProto = ShellTransitionsTraceFileProto.decode(traceBuffer) as any;
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) {
|
||||||
|
this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
|
||||||
|
} else {
|
||||||
|
console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto');
|
||||||
|
this.realToElapsedTimeOffsetNs = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handlerMapping = new Map<number, string>();
|
||||||
|
for (const mapping of decodedProto.handlerMappings) {
|
||||||
|
this.handlerMapping.set(mapping.id, mapping.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedProto.transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
|
||||||
|
return this.parseShellTransitionEntry(entryProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseShellTransitionEntry(entry: any): Transition {
|
||||||
|
this.validateShellTransitionEntry(entry);
|
||||||
|
|
||||||
|
if (this.realToElapsedTimeOffsetNs === undefined) {
|
||||||
|
throw new Error('missing realToElapsedTimeOffsetNs');
|
||||||
|
}
|
||||||
|
|
||||||
|
let dispatchTime = null;
|
||||||
|
if (entry.dispatchTimeNs && BigInt(entry.dispatchTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.dispatchTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
dispatchTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.dispatchTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mergeRequestTime = null;
|
||||||
|
if (entry.mergeRequestTimeNs && BigInt(entry.mergeRequestTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.mergeRequestTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
mergeRequestTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.mergeRequestTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mergeTime = null;
|
||||||
|
if (entry.mergeTimeNs && BigInt(entry.mergeTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.mergeTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
mergeTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.mergeTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let abortTime = null;
|
||||||
|
if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
abortTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.abortTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mergedInto = null;
|
||||||
|
if (entry.mergedInto !== 0) {
|
||||||
|
mergedInto = entry.mergedInto;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.handlerMapping === undefined) {
|
||||||
|
throw new Error('Missing handler mapping!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Transition(
|
||||||
|
entry.id,
|
||||||
|
new WmTransitionData(),
|
||||||
|
new ShellTransitionData(
|
||||||
|
dispatchTime,
|
||||||
|
mergeRequestTime,
|
||||||
|
mergeTime,
|
||||||
|
abortTime,
|
||||||
|
this.handlerMapping.get(entry.handler),
|
||||||
|
mergedInto
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateShellTransitionEntry(entry: any) {
|
||||||
|
if (entry.id === 0) {
|
||||||
|
throw new Error('Entry need a non null id');
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!entry.dispatchTimeNs &&
|
||||||
|
!entry.mergeRequestTimeNs &&
|
||||||
|
!entry.mergeTimeNs &&
|
||||||
|
!entry.abortTimeNs
|
||||||
|
) {
|
||||||
|
throw new Error('Requires at least one non-null timestamp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getMagicNumber(): number[] | undefined {
|
||||||
|
return [0x09, 0x57, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WMSTRACE
|
||||||
|
}
|
||||||
|
|
||||||
|
override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
|
||||||
|
decodedEntry = this.parseShellTransitionEntry(decodedEntry);
|
||||||
|
|
||||||
|
if (type === TimestampType.ELAPSED) {
|
||||||
|
return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === TimestampType.REAL) {
|
||||||
|
return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Timestamp type unsupported');
|
||||||
|
}
|
||||||
|
}
|
||||||
61
tools/winscope/src/parsers/parser_transitions_shell_test.ts
Normal file
61
tools/winscope/src/parsers/parser_transitions_shell_test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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 {UnitTestUtils} from 'test/unit/utils';
|
||||||
|
import {Parser} from 'trace/parser';
|
||||||
|
import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'trace/timestamp';
|
||||||
|
import {TraceType} from 'trace/trace_type';
|
||||||
|
|
||||||
|
describe('ShellFileParserTransitions', () => {
|
||||||
|
let parser: Parser<object>;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
parser = await UnitTestUtils.getParser(
|
||||||
|
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has expected trace type', () => {
|
||||||
|
expect(parser.getTraceType()).toEqual(TraceType.SHELL_TRANSITION);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides elapsed timestamps', () => {
|
||||||
|
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||||
|
|
||||||
|
expect(timestamps.length).toEqual(6);
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
new ElapsedTimestamp(57649649922341n),
|
||||||
|
new ElapsedTimestamp(57649829445249n),
|
||||||
|
new ElapsedTimestamp(57649829526223n),
|
||||||
|
];
|
||||||
|
expect(timestamps.slice(0, 3)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides real timestamps', () => {
|
||||||
|
const expected = [
|
||||||
|
new RealTimestamp(1683188477607285317n),
|
||||||
|
new RealTimestamp(1683188477786808225n),
|
||||||
|
new RealTimestamp(1683188477786889199n),
|
||||||
|
];
|
||||||
|
|
||||||
|
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
|
||||||
|
|
||||||
|
expect(timestamps.length).toEqual(6);
|
||||||
|
|
||||||
|
expect(timestamps.slice(0, 3)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 {UnitTestUtils} from 'test/unit/utils';
|
|
||||||
import {Parser} from 'trace/parser';
|
|
||||||
import {ElapsedTimestamp, TimestampType} from 'trace/timestamp';
|
|
||||||
import {TraceType} from 'trace/trace_type';
|
|
||||||
|
|
||||||
describe('ParserTransitions', () => {
|
|
||||||
describe('trace with elapsed (only) timestamp', () => {
|
|
||||||
let parser: Parser<object>;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
parser = await UnitTestUtils.getParser('traces/elapsed_timestamp/Transitions.pb');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has expected trace type', () => {
|
|
||||||
expect(parser.getTraceType()).toEqual(TraceType.TRANSITION);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('provides elapsed timestamps', () => {
|
|
||||||
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
|
||||||
|
|
||||||
expect(timestamps.length).toEqual(7);
|
|
||||||
|
|
||||||
const expected = [
|
|
||||||
new ElapsedTimestamp(1862299518404n),
|
|
||||||
new ElapsedTimestamp(1863412780164n),
|
|
||||||
new ElapsedTimestamp(1865439877129n),
|
|
||||||
];
|
|
||||||
expect(timestamps.slice(0, 3)).toEqual(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("doesn't provide real timestamps", () => {
|
|
||||||
expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
149
tools/winscope/src/parsers/parser_transitions_wm.ts
Normal file
149
tools/winscope/src/parsers/parser_transitions_wm.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import {
|
||||||
|
CrossPlatform,
|
||||||
|
ShellTransitionData,
|
||||||
|
Transition,
|
||||||
|
TransitionChange,
|
||||||
|
TransitionType,
|
||||||
|
WmTransitionData,
|
||||||
|
} from 'trace/flickerlib/common';
|
||||||
|
import {ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType} from 'trace/timestamp';
|
||||||
|
import {TraceType} from 'trace/trace_type';
|
||||||
|
import {AbstractParser} from './abstract_parser';
|
||||||
|
import {WmTransitionsTraceFileProto} from './proto_types';
|
||||||
|
|
||||||
|
export class ParserTransitionsWm extends AbstractParser {
|
||||||
|
private realToElapsedTimeOffsetNs: undefined | bigint;
|
||||||
|
|
||||||
|
override getTraceType(): TraceType {
|
||||||
|
return TraceType.WM_TRANSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
|
||||||
|
return this.parseWmTransitionEntry(entryProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
override decodeTrace(buffer: Uint8Array): any[] {
|
||||||
|
const decodedProto = WmTransitionsTraceFileProto.decode(buffer) as any;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(decodedProto, 'realToElapsedTimeOffsetNanos')) {
|
||||||
|
this.realToElapsedTimeOffsetNs = BigInt(decodedProto.realToElapsedTimeOffsetNanos);
|
||||||
|
} else {
|
||||||
|
console.warn('Missing realToElapsedTimeOffsetNanos property on SF trace proto');
|
||||||
|
this.realToElapsedTimeOffsetNs = undefined;
|
||||||
|
}
|
||||||
|
return decodedProto.transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getMagicNumber(): number[] | undefined {
|
||||||
|
return [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE
|
||||||
|
}
|
||||||
|
|
||||||
|
override getTimestamp(type: TimestampType, decodedEntry: Transition): undefined | Timestamp {
|
||||||
|
decodedEntry = this.parseWmTransitionEntry(decodedEntry);
|
||||||
|
|
||||||
|
if (type === TimestampType.ELAPSED) {
|
||||||
|
return new ElapsedTimestamp(BigInt(decodedEntry.timestamp.elapsedNanos.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === TimestampType.REAL) {
|
||||||
|
return new RealTimestamp(BigInt(decodedEntry.timestamp.unixNanos.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Timestamp type unsupported');
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseWmTransitionEntry(entry: any): Transition {
|
||||||
|
this.validateWmTransitionEntry(entry);
|
||||||
|
let changes: TransitionChange[] | null;
|
||||||
|
if (entry.targets.length === 0) {
|
||||||
|
changes = null;
|
||||||
|
} else {
|
||||||
|
changes = entry.targets.map(
|
||||||
|
(it: any) =>
|
||||||
|
new TransitionChange(TransitionType.Companion.fromInt(it.mode), it.layerId, it.windowId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.realToElapsedTimeOffsetNs === undefined) {
|
||||||
|
throw new Error('missing realToElapsedTimeOffsetNs');
|
||||||
|
}
|
||||||
|
|
||||||
|
let createTime = null;
|
||||||
|
if (entry.createTimeNs && BigInt(entry.createTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.createTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
createTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.createTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendTime = null;
|
||||||
|
if (entry.sendTimeNs && BigInt(entry.sendTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.sendTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
sendTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.sendTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let abortTime = null;
|
||||||
|
if (entry.abortTimeNs && BigInt(entry.abortTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.abortTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
abortTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.abortTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let finishTime = null;
|
||||||
|
if (entry.finishTimeNs && BigInt(entry.finishTimeNs.toString()) !== 0n) {
|
||||||
|
const unixNs = BigInt(entry.finishTimeNs.toString()) + this.realToElapsedTimeOffsetNs;
|
||||||
|
finishTime = CrossPlatform.timestamp.fromString(
|
||||||
|
entry.finishTimeNs.toString(),
|
||||||
|
null,
|
||||||
|
unixNs.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let startTransactionId = null;
|
||||||
|
if (entry.startTransactionId !== 0) {
|
||||||
|
startTransactionId = entry.startTransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
let finishTransactionId = null;
|
||||||
|
if (entry.finishTransactionId !== 0) {
|
||||||
|
finishTransactionId = entry.finishTransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type = null;
|
||||||
|
if (entry.type !== 0) {
|
||||||
|
type = TransitionType.Companion.fromInt(entry.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Transition(
|
||||||
|
entry.id,
|
||||||
|
new WmTransitionData(
|
||||||
|
createTime,
|
||||||
|
sendTime,
|
||||||
|
abortTime,
|
||||||
|
finishTime,
|
||||||
|
startTransactionId,
|
||||||
|
finishTransactionId,
|
||||||
|
type,
|
||||||
|
changes
|
||||||
|
),
|
||||||
|
new ShellTransitionData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateWmTransitionEntry(entry: any) {
|
||||||
|
if (entry.id === 0) {
|
||||||
|
throw new Error('Entry need a non null id');
|
||||||
|
}
|
||||||
|
if (!entry.createTimeNs && !entry.sendTimeNs && !entry.abortTimeNs && !entry.finishTimeNs) {
|
||||||
|
throw new Error('Requires at least one non-null timestamp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
tools/winscope/src/parsers/parser_transitions_wm_test.ts
Normal file
61
tools/winscope/src/parsers/parser_transitions_wm_test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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 {UnitTestUtils} from 'test/unit/utils';
|
||||||
|
import {Parser} from 'trace/parser';
|
||||||
|
import {ElapsedTimestamp, RealTimestamp, TimestampType} from 'trace/timestamp';
|
||||||
|
import {TraceType} from 'trace/trace_type';
|
||||||
|
|
||||||
|
describe('WmFileParserTransitions', () => {
|
||||||
|
let parser: Parser<object>;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
parser = await UnitTestUtils.getParser(
|
||||||
|
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has expected trace type', () => {
|
||||||
|
expect(parser.getTraceType()).toEqual(TraceType.WM_TRANSITION);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides elapsed timestamps', () => {
|
||||||
|
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
|
||||||
|
|
||||||
|
expect(timestamps.length).toEqual(8);
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
new ElapsedTimestamp(57649586217344n),
|
||||||
|
new ElapsedTimestamp(57649691956439n),
|
||||||
|
new ElapsedTimestamp(57650183020323n),
|
||||||
|
];
|
||||||
|
expect(timestamps.slice(0, 3)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides real timestamps', () => {
|
||||||
|
const expected = [
|
||||||
|
new RealTimestamp(1683188477542869667n),
|
||||||
|
new RealTimestamp(1683188477648608762n),
|
||||||
|
new RealTimestamp(1683188478139672646n),
|
||||||
|
];
|
||||||
|
|
||||||
|
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
|
||||||
|
|
||||||
|
expect(timestamps.length).toEqual(8);
|
||||||
|
|
||||||
|
expect(timestamps.slice(0, 3)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -22,8 +22,9 @@ protobuf.configure();
|
|||||||
import protoLogJson from 'frameworks/base/core/proto/android/internal/protolog.proto';
|
import protoLogJson from 'frameworks/base/core/proto/android/internal/protolog.proto';
|
||||||
import accessibilityJson from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
|
import accessibilityJson from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
|
||||||
import windowManagerJson from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
|
import windowManagerJson from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
|
||||||
import transitionsJson from 'frameworks/base/core/proto/android/server/windowmanagertransitiontrace.proto';
|
import wmTransitionsJson from 'frameworks/base/core/proto/android/server/windowmanagertransitiontrace.proto';
|
||||||
import inputMethodClientsJson from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
|
import inputMethodClientsJson from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
|
||||||
|
import shellTransitionsJson from 'frameworks/base/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto';
|
||||||
import layersJson from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
|
import layersJson from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
|
||||||
import transactionsJson from 'frameworks/native/services/surfaceflinger/layerproto/transactions.proto';
|
import transactionsJson from 'frameworks/native/services/surfaceflinger/layerproto/transactions.proto';
|
||||||
|
|
||||||
@@ -54,9 +55,12 @@ const WindowManagerServiceDumpProto = protobuf.Root.fromJSON(windowManagerJson).
|
|||||||
const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType(
|
const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType(
|
||||||
'com.android.server.wm.WindowManagerTraceFileProto'
|
'com.android.server.wm.WindowManagerTraceFileProto'
|
||||||
);
|
);
|
||||||
const TransitionsTraceFileProto = protobuf.Root.fromJSON(transitionsJson).lookupType(
|
const WmTransitionsTraceFileProto = protobuf.Root.fromJSON(wmTransitionsJson).lookupType(
|
||||||
'com.android.server.wm.shell.TransitionTraceProto'
|
'com.android.server.wm.shell.TransitionTraceProto'
|
||||||
);
|
);
|
||||||
|
const ShellTransitionsTraceFileProto = protobuf.Root.fromJSON(shellTransitionsJson).lookupType(
|
||||||
|
'com.android.wm.shell.WmShellTransitionTraceProto'
|
||||||
|
);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AccessibilityTraceFileProto,
|
AccessibilityTraceFileProto,
|
||||||
@@ -68,5 +72,6 @@ export {
|
|||||||
TransactionsTraceFileProto,
|
TransactionsTraceFileProto,
|
||||||
WindowManagerServiceDumpProto,
|
WindowManagerServiceDumpProto,
|
||||||
WindowManagerTraceFileProto,
|
WindowManagerTraceFileProto,
|
||||||
TransitionsTraceFileProto,
|
WmTransitionsTraceFileProto,
|
||||||
|
ShellTransitionsTraceFileProto,
|
||||||
};
|
};
|
||||||
|
|||||||
2
tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/shell_transition_trace.pb
vendored
Normal file
2
tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/shell_transition_trace.pb
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
WMSTRACE<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
7
tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/wm_transition_trace.pb
vendored
Normal file
7
tools/winscope/src/test/fixtures/traces/elapsed_and_real_timestamp/wm_transition_trace.pb
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
TRNTRACE#<23><><06><>[? <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>8B
|
||||||
|
<10>֒<>XB q<18><><EFBFBD>UF ש<><D7A9><EFBFBD><EFBFBD>
|
||||||
|
(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
<10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><18><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>8B <20><><10>ۉ<>/B <20><><10><18><><EFBFBD>
|
||||||
|
0<><30><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
@@ -67,6 +67,9 @@ const EventLogParser = require('flicker').android.tools.common.parsers.events.Ev
|
|||||||
const Transition = require('flicker').android.tools.common.traces.wm.Transition;
|
const Transition = require('flicker').android.tools.common.traces.wm.Transition;
|
||||||
const TransitionType = require('flicker').android.tools.common.traces.wm.TransitionType;
|
const TransitionType = require('flicker').android.tools.common.traces.wm.TransitionType;
|
||||||
const TransitionChange = require('flicker').android.tools.common.traces.wm.TransitionChange;
|
const TransitionChange = require('flicker').android.tools.common.traces.wm.TransitionChange;
|
||||||
|
const TransitionsTrace = require('flicker').android.tools.common.traces.wm.TransitionsTrace;
|
||||||
|
const ShellTransitionData = require('flicker').android.tools.common.traces.wm.ShellTransitionData;
|
||||||
|
const WmTransitionData = require('flicker').android.tools.common.traces.wm.WmTransitionData;
|
||||||
|
|
||||||
// Common
|
// Common
|
||||||
const Size = require('flicker').android.tools.common.datatypes.Size;
|
const Size = require('flicker').android.tools.common.datatypes.Size;
|
||||||
@@ -314,6 +317,9 @@ export {
|
|||||||
Transition,
|
Transition,
|
||||||
TransitionType,
|
TransitionType,
|
||||||
TransitionChange,
|
TransitionChange,
|
||||||
|
TransitionsTrace,
|
||||||
|
ShellTransitionData,
|
||||||
|
WmTransitionData,
|
||||||
// Common
|
// Common
|
||||||
Size,
|
Size,
|
||||||
ActiveBuffer,
|
ActiveBuffer,
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export enum TraceType {
|
|||||||
INPUT_METHOD_MANAGER_SERVICE,
|
INPUT_METHOD_MANAGER_SERVICE,
|
||||||
INPUT_METHOD_SERVICE,
|
INPUT_METHOD_SERVICE,
|
||||||
EVENT_LOG,
|
EVENT_LOG,
|
||||||
TRANSITION,
|
WM_TRANSITION,
|
||||||
|
SHELL_TRANSITION,
|
||||||
TAG,
|
TAG,
|
||||||
ERROR,
|
ERROR,
|
||||||
TEST_TRACE_STRING,
|
TEST_TRACE_STRING,
|
||||||
@@ -59,7 +60,8 @@ export interface TraceEntryTypeMap {
|
|||||||
[TraceType.INPUT_METHOD_MANAGER_SERVICE]: object;
|
[TraceType.INPUT_METHOD_MANAGER_SERVICE]: object;
|
||||||
[TraceType.INPUT_METHOD_SERVICE]: object;
|
[TraceType.INPUT_METHOD_SERVICE]: object;
|
||||||
[TraceType.EVENT_LOG]: Event;
|
[TraceType.EVENT_LOG]: Event;
|
||||||
[TraceType.TRANSITION]: Transition;
|
[TraceType.WM_TRANSITION]: object;
|
||||||
|
[TraceType.SHELL_TRANSITION]: object;
|
||||||
[TraceType.TAG]: object;
|
[TraceType.TAG]: object;
|
||||||
[TraceType.ERROR]: object;
|
[TraceType.ERROR]: object;
|
||||||
[TraceType.TEST_TRACE_STRING]: string;
|
[TraceType.TEST_TRACE_STRING]: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user