add support for traces real timestamp

Parsers support real timestamps (along the legacy elapsed timestamps) for the following trace types:
- Accessibility
- InputMethodClients
- InputMethodManagerService
- InputMethodService
- ProtoLog
- SurfaceFlinger
- Transactions
- WindowManager

Bug: 235196806
Test: cd development/tools/winscope-ng && npm run build:unit && npm run test:unit
Change-Id: Ib26fa7c4b8b4cc0ed3128c6805c57fcb1ce7ae85
This commit is contained in:
Kean Mariotti
2022-07-29 08:17:04 +00:00
parent 31392005c5
commit 5415b8bf69
48 changed files with 751 additions and 320 deletions

View File

@@ -41,7 +41,7 @@ abstract class Parser {
let areTimestampsValid = true;
for (const entry of this.decodedEntries) {
const timestamp = this.getTimestamp(entry, type);
const timestamp = this.getTimestamp(type, entry);
if (timestamp === undefined) {
areTimestampsValid = false;
break;
@@ -82,8 +82,7 @@ abstract class Parser {
protected abstract getMagicNumber(): undefined|number[];
protected abstract decodeTrace(trace: Uint8Array): any[];
//TODO: invert parameters below
protected abstract getTimestamp(decodedEntry: any, type: TimestampType): undefined|Timestamp;
protected abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined|Timestamp;
protected abstract processDecodedEntry(decodedEntry: any): any;
protected trace: Blob;

View File

@@ -16,34 +16,72 @@
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserAccessibility", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/Accessibility.pb");
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/Accessibility.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
});
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 14499089524n),
new Timestamp(TimestampType.ELAPSED, 14499599656n),
new Timestamp(TimestampType.ELAPSED, 14953120693n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
.toEqual(expected);
});
it("provides real timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107089100052652n),
new Timestamp(TimestampType.REAL, 1659107089100562784n),
new Timestamp(TimestampType.REAL, 1659107089554083821n),
];
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 14499599656n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(14499599656n);
});
it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089100562784n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(14499599656n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850297444302n),
new Timestamp(TimestampType.ELAPSED, 850297882046n),
new Timestamp(TimestampType.ELAPSED, 850756176154n),
new Timestamp(TimestampType.ELAPSED, 850773581835n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/Accessibility.pb");
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850297444302n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(850297444302n);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.ACCESSIBILITY);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 850297444302n));
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
});
});

View File

@@ -21,6 +21,7 @@ import {AccessibilityTraceFileProto} from "./proto_types";
class ParserAccessibility extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
override getTraceType(): TraceType {
@@ -32,20 +33,31 @@ class ParserAccessibility extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>AccessibilityTraceFileProto.decode(buffer)).entry;
const decoded = <any>AccessibilityTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
override processDecodedEntry(entryProto: any): any {
return entryProto;
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
}

View File

@@ -14,57 +14,97 @@
* limitations under the License.
*/
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
describe("Parser", () => {
let parser: Parser;
describe("real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_WindowManager.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/WindowManager.pb");
});
it("provides timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107089075566202n),
new Timestamp(TimestampType.REAL, 1659107089999048990n),
new Timestamp(TimestampType.REAL, 1659107090010194213n)
];
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry (no timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089075566201n);
expect(parser.getTraceEntry(timestamp))
.toEqual(undefined);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089075566202n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(14474594000n);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(15398076788n);
});
it("retrieves trace entry (lower timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048991n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(15398076788n);
});
});
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850254319343n),
new Timestamp(TimestampType.ELAPSED, 850763506110n),
new Timestamp(TimestampType.ELAPSED, 850782750048n)
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
describe("elapsed timestamp", () => {
let parser: Parser;
it("retrieves trace entry (no timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319342n);
expect(parser.getTraceEntry(timestamp))
.toEqual(undefined);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/WindowManager.pb");
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850254319343n);
});
it("provides timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850254319343n),
new Timestamp(TimestampType.ELAPSED, 850763506110n),
new Timestamp(TimestampType.ELAPSED, 850782750048n)
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506110n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850763506110n);
});
it("retrieves trace entry (no timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319342n);
expect(parser.getTraceEntry(timestamp))
.toEqual(undefined);
});
it("retrieves trace entry (lower timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319344n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850254319343n);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850254319343n);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506111n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850763506110n);
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506110n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850763506110n);
});
it("retrieves trace entry (lower timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319344n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850254319343n);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850763506111n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
.toEqual(850763506110n);
});
});
});

View File

@@ -15,40 +15,76 @@
*/
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserInputMethodlClients", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_InputMethodClients.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodClients.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)!.length)
.toEqual(13);
const expected = [
new Timestamp(TimestampType.ELAPSED, 15613638434n),
new Timestamp(TimestampType.ELAPSED, 15647516364n),
new Timestamp(TimestampType.ELAPSED, 15677650967n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
.toEqual(expected);
});
it("provides real timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107090215405395n),
new Timestamp(TimestampType.REAL, 1659107090249283325n),
new Timestamp(TimestampType.REAL, 1659107090279417928n),
];
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 15647516364n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(15647516364n);
});
it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107090249283325n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(15647516364n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)!.length)
.toEqual(33);
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodClients.pb");
});
const expected = [
new Timestamp(TimestampType.ELAPSED, 1149083651642n),
new Timestamp(TimestampType.ELAPSED, 1149083950633n),
new Timestamp(TimestampType.ELAPSED, 1149127567251n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
.toEqual(expected);
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 1149083651642n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(1149083651642n);
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149083651642n));
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149083651642n));
});
});
});

View File

@@ -21,6 +21,7 @@ import {InputMethodClientsTraceFileProto} from "./proto_types";
class ParserInputMethodClients extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
getTraceType(): TraceType {
@@ -32,20 +33,32 @@ class ParserInputMethodClients extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
const decoded = <any>InputMethodClientsTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return (<any>InputMethodClientsTraceFileProto.decode(buffer)).entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs != undefined) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs);
}
return undefined;
}
override processDecodedEntry(entryProto: any): any {
return entryProto;
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
}

View File

@@ -15,37 +15,63 @@
*/
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserInputMethodManagerService", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_InputMethodManagerService.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodManagerService.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual([new Timestamp(TimestampType.ELAPSED, 15963782518n)]);
});
it("provides real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual([new Timestamp(TimestampType.REAL, 1659107090565549479n)]);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 15963782518n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(15963782518n);
});
it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107090565549479n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(15963782518n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 1149226290110n),
new Timestamp(TimestampType.ELAPSED, 1149237707591n),
new Timestamp(TimestampType.ELAPSED, 1149238950389n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodManagerService.pb");
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 1149226290110n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(1149226290110n);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_MANAGER_SERVICE);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149226290110n));
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
});
});

View File

@@ -21,6 +21,7 @@ import {InputMethodManagerServiceTraceFileProto} from "./proto_types";
class ParserInputMethodManagerService extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
getTraceType(): TraceType {
@@ -32,20 +33,31 @@ class ParserInputMethodManagerService extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>InputMethodManagerServiceTraceFileProto.decode(buffer)).entry;
const decoded = <any>InputMethodManagerServiceTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
protected override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
protected override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
protected override processDecodedEntry(entryProto: any): any {
return entryProto;
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
}

View File

@@ -20,36 +20,65 @@ import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserInputMethodService", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_InputMethodService.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/InputMethodService.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
});
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 16578752896n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
it("provides real timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107091180519857n),
];
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 16578752896n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(16578752896n);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107091180519857n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(16578752896n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 1149230019887n),
new Timestamp(TimestampType.ELAPSED, 1149234359324n),
new Timestamp(TimestampType.ELAPSED, 1149241227244n),
new Timestamp(TimestampType.ELAPSED, 1149243083608n),
new Timestamp(TimestampType.ELAPSED, 1149249518016n),
new Timestamp(TimestampType.ELAPSED, 1149249784617n),
new Timestamp(TimestampType.ELAPSED, 1149272993520n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/InputMethodService.pb");
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 1149230019887n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(1149230019887n);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 1149230019887n));
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
});
});

View File

@@ -21,6 +21,7 @@ import {InputMethodServiceTraceFileProto} from "./proto_types";
class ParserInputMethodService extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
getTraceType(): TraceType {
@@ -32,20 +33,31 @@ class ParserInputMethodService extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>InputMethodServiceTraceFileProto.decode(buffer)).entry;
const decoded = <any>InputMethodServiceTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
override processDecodedEntry(entryProto: any): any {
return entryProto;
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
}

View File

@@ -15,7 +15,6 @@
*/
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
import {LogMessage} from "../common/trace/protolog";
@@ -33,10 +32,7 @@ describe("ParserProtoLog", () => {
};
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_ProtoLog.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/ProtoLog.pb");
});
it("has expected trace type", () => {
@@ -57,6 +53,20 @@ describe("ParserProtoLog", () => {
.toEqual(expected);
});
it("provides real timestamps", () => {
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
expect(timestamps.length)
.toEqual(50);
const expected = [
new Timestamp(TimestampType.REAL, 1655727125377266486n),
new Timestamp(TimestampType.REAL, 1655727125377336718n),
new Timestamp(TimestampType.REAL, 1655727125377350430n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
});
it("reconstructs human-readable log message", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850746266486n);
const actualMessage = parser.getTraceEntry(timestamp)!;

View File

@@ -48,6 +48,8 @@ class ParserProtoLog extends Parser {
throw new TypeError(message);
}
this.realToElapsedTimeOffsetNs = BigInt(fileProto.realTimeToElapsedTimeOffsetMillis) * 1000000n;
fileProto.log.sort((a: any, b: any) => {
return Number(a.elapsedRealtimeNanos) - Number(b.elapsedRealtimeNanos);
});
@@ -55,11 +57,14 @@ class ParserProtoLog extends Parser {
return fileProto.log;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type == TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
else if (type == TimestampType.REAL) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs!);
}
return undefined;
}
override processDecodedEntry(entryProto: any): LogMessage {
@@ -85,6 +90,7 @@ class ParserProtoLog extends Parser {
});
}
private realToElapsedTimeOffsetNs: undefined|bigint = undefined;
private static readonly MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
private static readonly PROTOLOG_VERSION = "1.0.0";
}

View File

@@ -18,16 +18,12 @@ import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {UnitTestUtils} from "test/unit/utils";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
describe("ParserScreenRecording", () => {
let parser: Parser;
beforeAll(async () => {
const trace = UnitTestUtils.getFixtureBlob("screen_recording.mp4");
const parsers = await new ParserFactory().createParsers([trace]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/screen_recording.mp4");
});
it("has expected trace type", () => {
@@ -38,12 +34,12 @@ describe("ParserScreenRecording", () => {
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
expect(timestamps.length)
.toEqual(88);
.toEqual(15);
const expected = [
new Timestamp(TimestampType.ELAPSED, 732949304000n),
new Timestamp(TimestampType.ELAPSED, 733272129000n),
new Timestamp(TimestampType.ELAPSED, 733283916000n),
new Timestamp(TimestampType.ELAPSED, 144857685000n),
new Timestamp(TimestampType.ELAPSED, 144866679000n),
new Timestamp(TimestampType.ELAPSED, 144875772000n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
@@ -53,12 +49,12 @@ describe("ParserScreenRecording", () => {
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
expect(timestamps.length)
.toEqual(88);
.toEqual(15);
const expected = [
new Timestamp(TimestampType.REAL, 1658843852566916386n),
new Timestamp(TimestampType.REAL, 1658843852889741386n),
new Timestamp(TimestampType.REAL, 1658843852901528386n),
new Timestamp(TimestampType.REAL, 1659687791485257266n),
new Timestamp(TimestampType.REAL, 1659687791494251266n),
new Timestamp(TimestampType.REAL, 1659687791503344266n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
@@ -66,30 +62,30 @@ describe("ParserScreenRecording", () => {
it("retrieves trace entry from elapsed timestamp", () => {
{
const timestamp = new Timestamp(TimestampType.ELAPSED, 732949304000n);
const timestamp = new Timestamp(TimestampType.ELAPSED, 144857685000n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
}
{
const timestamp = new Timestamp(TimestampType.ELAPSED, 733272129000n);
const timestamp = new Timestamp(TimestampType.ELAPSED, 145300550000n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.322, 0.001);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.442, 0.001);
}
});
it("retrieves trace entry from real timestamp", () => {
{
const timestamp = new Timestamp(TimestampType.REAL, 1658843852566916386n);
const timestamp = new Timestamp(TimestampType.REAL, 1659687791485257266n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
}
{
const timestamp = new Timestamp(TimestampType.REAL, 1658843852889741386n);
const timestamp = new Timestamp(TimestampType.REAL, 1659687791928122266n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0.322, 0.001);

View File

@@ -52,19 +52,19 @@ class ParserScreenRecording extends Parser {
});
}
override getTimestamp(decodedEntry: ScreenRecordingMetadataEntry, type: TimestampType): undefined|Timestamp {
override getTimestamp(type: TimestampType, decodedEntry: ScreenRecordingMetadataEntry): undefined|Timestamp {
if (type !== TimestampType.ELAPSED && type !== TimestampType.REAL) {
return undefined;
}
if (type === TimestampType.ELAPSED) {
// Traces typically contain "elapsed" timestamps (SYSTEM_TIME_BOOTTIME).
// Screen recordings contain SYSTEM_TIME_MONOTONIC timestamps.
// Traces typically contain "elapsed" timestamps (SYSTEM_TIME_BOOTTIME),
// whereas screen recordings contain SYSTEM_TIME_MONOTONIC timestamps.
//
// Here we are pretending that screen recordings contain "elapsed" timestamps
// as well, in order to synchronize with the other traces.
//
// If no device suspensions are involved, SYSTEM_TIME_MONOTONIC should indeed
// correspond to SYSTEM_TIME_BOOTTIME and things should work as expected.
// correspond to SYSTEM_TIME_BOOTTIME and things will work as expected.
return new Timestamp(type, decodedEntry.timestampMonotonicNs);
}
else if (type === TimestampType.REAL) {
@@ -125,8 +125,6 @@ class ParserScreenRecording extends Parser {
for (let i = 0; i < count; ++i) {
const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8);
pos += 8;
//parse VSYNC ID here when available
pos += 8;
timestamps.push(timestamp);
}
return timestamps;

View File

@@ -18,16 +18,12 @@ import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {UnitTestUtils} from "test/unit/utils";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
describe("ParserScreenRecordingLegacy", () => {
let parser: Parser;
beforeAll(async () => {
const trace = UnitTestUtils.getFixtureBlob("screen_recording_legacy.mp4");
const parsers = await new ParserFactory().createParsers([trace]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/screen_recording.mp4");
});
it("has expected trace type", () => {

View File

@@ -38,7 +38,7 @@ class ParserScreenRecordingLegacy extends Parser {
return this.parseTimestamps(videoData, posTimestamps, count);
}
override getTimestamp(decodedEntry: Timestamp, type: TimestampType): undefined|Timestamp {
override getTimestamp(type: TimestampType, decodedEntry: Timestamp): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
}

View File

@@ -18,36 +18,73 @@ import {TraceType} from "common/trace/trace_type";
import {LayerTraceEntry} from "common/trace/flickerlib/layers/LayerTraceEntry";
import {UnitTestUtils} from "test/unit/utils";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
describe("ParserSurfaceFlinger", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_SurfaceFlinger.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/SurfaceFlinger.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 14500282843n),
new Timestamp(TimestampType.ELAPSED, 14631249355n),
new Timestamp(TimestampType.ELAPSED, 15403446377n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
.toEqual(expected);
});
it("provides real timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107089102062832n),
new Timestamp(TimestampType.REAL, 1659107089233029344n),
new Timestamp(TimestampType.REAL, 1659107090005226366n),
];
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 14631249355n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089233029344n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850335483446n),
new Timestamp(TimestampType.ELAPSED, 850686322883n),
new Timestamp(TimestampType.ELAPSED, 850736507697n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/SurfaceFlinger.pb");
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850335483446n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(850335483446n);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
it("provides elapsed timestamps", () => {
expect(parser.getTimestamps(TimestampType.ELAPSED)![0])
.toEqual(new Timestamp(TimestampType.ELAPSED, 850335483446n));
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
});
});

View File

@@ -22,6 +22,7 @@ import {LayersTraceFileProto} from "./proto_types";
class ParserSurfaceFlinger extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
override getTraceType(): TraceType {
@@ -33,20 +34,36 @@ class ParserSurfaceFlinger extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>LayersTraceFileProto.decode(buffer)).entry;
const decoded = <any>LayersTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
const isDump = !Object.prototype.hasOwnProperty.call(entryProto, "elapsedRealtimeNanos");
if (type === TimestampType.ELAPSED) {
return isDump
? new Timestamp(type, 0n)
: new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(TimestampType.ELAPSED, entryProto.elapsedRealtimeNanos);
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return isDump
? new Timestamp(type, 0n)
: new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
override processDecodedEntry(entryProto: any): any {
return LayerTraceEntry.fromProto(entryProto.layers.layers, entryProto.displays, entryProto.elapsedRealtimeNanos, entryProto.hwcBlob);
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
}

View File

@@ -18,35 +18,68 @@ import {TraceType} from "common/trace/trace_type";
import {LayerTraceEntry} from "common/trace/flickerlib/layers/LayerTraceEntry";
import {UnitTestUtils} from "test/unit/utils";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
describe("ParserSurfaceFlingerDump", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("dump_SurfaceFlinger.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
it("provides elapsed timestamp", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 0n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
});
it("provides real timestamp (always zero)", () => {
const expected = [
new Timestamp(TimestampType.REAL, 0n),
];
expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(0n);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(0n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides timestamp", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 0n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/dump_SurfaceFlinger.pb");
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(BigInt(entry.timestampMs)).toEqual(0n);
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.SURFACE_FLINGER);
});
//TODO: add real timestamp
it("provides elapsed timestamp (always zero)", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 0n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
});
it("doesn't provide real timestamp", () => {
expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
});
});
});

View File

@@ -16,41 +16,92 @@
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {Parser} from "./parser";
import {ParserFactory} from "./parser_factory";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserTransactions", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_Transactions.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/Transactions.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
});
it("provides elapsed timestamps", () => {
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
expect(timestamps.length)
.toEqual(712);
const expected = [
new Timestamp(TimestampType.ELAPSED, 2450981445n),
new Timestamp(TimestampType.ELAPSED, 2517952515n),
new Timestamp(TimestampType.ELAPSED, 4021151449n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
});
it("provides real timestamps", () => {
const timestamps = parser.getTimestamps(TimestampType.REAL)!;
expect(timestamps.length)
.toEqual(712);
const expected = [
new Timestamp(TimestampType.REAL, 1659507541051480997n),
new Timestamp(TimestampType.REAL, 1659507541118452067n),
new Timestamp(TimestampType.REAL, 1659507542621651001n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry from elsapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 2517952515n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(2517952515n);
});
it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659507541118452067n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(2517952515n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
});
describe("trace with elapsed (only) timestamp", () => {
let parser: Parser;
it("provides timestamps", () => {
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/Transactions.pb");
});
expect(timestamps.length)
.toEqual(4997);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
});
const expected = [
new Timestamp(TimestampType.ELAPSED, 14862317023n),
new Timestamp(TimestampType.ELAPSED, 14873423549n),
new Timestamp(TimestampType.ELAPSED, 14884850511n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
});
it("provides elapsed timestamps", () => {
const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 14862317023n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(14862317023n);
expect(timestamps.length)
.toEqual(4997);
const expected = [
new Timestamp(TimestampType.ELAPSED, 14862317023n),
new Timestamp(TimestampType.ELAPSED, 14873423549n),
new Timestamp(TimestampType.ELAPSED, 14884850511n),
];
expect(timestamps.slice(0, 3))
.toEqual(expected);
});
it("doesn't provide real timestamps", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
});
});

View File

@@ -16,11 +16,12 @@
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {Parser} from "./parser";
import {TransactionsTraceFileProto} from "./proto_types";
import {AccessibilityTraceFileProto, TransactionsTraceFileProto} from "./proto_types";
class ParserTransactions extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
override getTraceType(): TraceType {
@@ -32,20 +33,31 @@ class ParserTransactions extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>TransactionsTraceFileProto.decode(buffer)).entry;
const decoded = <any>TransactionsTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
override processDecodedEntry(entryProto: any): any {
return entryProto;
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x54, 0x4e, 0x58, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TNXTRACE
}

View File

@@ -16,38 +16,82 @@
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
describe("ParserWindowManager", () => {
let parser: Parser;
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("trace_WindowManager.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/WindowManager.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
});
it("provides elapsed timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 14474594000n),
new Timestamp(TimestampType.ELAPSED, 15398076788n),
new Timestamp(TimestampType.ELAPSED, 15409222011n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED)!.slice(0, 3))
.toEqual(expected);
});
it("provides real timestamps", () => {
const expected = [
new Timestamp(TimestampType.REAL, 1659107089075566202n),
new Timestamp(TimestampType.REAL, 1659107089999048990n),
new Timestamp(TimestampType.REAL, 1659107090010194213n),
];
expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3))
.toEqual(expected);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 15398076788n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(15398076788n);
});
it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(15398076788n);
});
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
});
describe("trace elapsed timestamp", () => {
let parser: Parser;
it("provides timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850254319343n),
new Timestamp(TimestampType.ELAPSED, 850763506110n),
new Timestamp(TimestampType.ELAPSED, 850782750048n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_timestamp/WindowManager.pb");
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(850254319343n);
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
});
it("provides timestamps", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 850254319343n),
new Timestamp(TimestampType.ELAPSED, 850763506110n),
new Timestamp(TimestampType.ELAPSED, 850782750048n),
];
expect(parser.getTimestamps(TimestampType.ELAPSED))
.toEqual(expected);
});
it("retrieves trace entry", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(850254319343n);
});
});
});

View File

@@ -22,6 +22,7 @@ import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerS
class ParserWindowManager extends Parser {
constructor(trace: Blob) {
super(trace);
this.realToElapsedTimeOffsetNs = undefined;
}
override getTraceType(): TraceType {
@@ -33,20 +34,31 @@ class ParserWindowManager extends Parser {
}
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>WindowManagerTraceFileProto.decode(buffer)).entry;
const decoded = <any>WindowManagerTraceFileProto.decode(buffer);
if (Object.prototype.hasOwnProperty.call(decoded, "realToElapsedTimeOffsetNanos")) {
this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos);
}
else {
this.realToElapsedTimeOffsetNs = undefined;
}
return decoded.entry;
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type === TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
return new Timestamp(TimestampType.ELAPSED, BigInt(entryProto.elapsedRealtimeNanos));
else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
return new Timestamp(type, this.realToElapsedTimeOffsetNs + BigInt(entryProto.elapsedRealtimeNanos));
}
return undefined;
}
override processDecodedEntry(entryProto: any): WindowManagerState {
return WindowManagerState.fromProto(entryProto.windowManagerService, entryProto.elapsedRealtimeNanos, entryProto.where);
}
private realToElapsedTimeOffsetNs: undefined|bigint;
private static readonly MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
}

View File

@@ -16,7 +16,6 @@
import {WindowManagerState} from "common/trace/flickerlib/windows/WindowManagerState";
import {Timestamp, TimestampType} from "common/trace/timestamp";
import {TraceType} from "common/trace/trace_type";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {UnitTestUtils} from "test/unit/utils";
@@ -24,17 +23,14 @@ describe("ParserWindowManagerDump", () => {
let parser: Parser;
beforeAll(async () => {
const buffer = UnitTestUtils.getFixtureBlob("dump_WindowManager.pb");
const parsers = await new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
parser = await UnitTestUtils.getParser("traces/dump_WindowManager.pb");
});
it("has expected trace type", () => {
expect(parser.getTraceType()).toEqual(TraceType.WINDOW_MANAGER);
});
it("provides timestamps", () => {
it("provides elapsed timestamp (always zero)", () => {
const expected = [
new Timestamp(TimestampType.ELAPSED, 0n),
];
@@ -42,12 +38,15 @@ describe("ParserWindowManagerDump", () => {
.toEqual(expected);
});
it("retrieves trace entry", () => {
it("doesn't provide real timestamp (never)", () => {
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
it("retrieves trace entry from elapsed timestamp", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(0n);
});
//TODO: add real timestamp
});

View File

@@ -36,7 +36,7 @@ class ParserWindowManagerDump extends Parser {
return [WindowManagerServiceDumpProto.decode(buffer)];
}
override getTimestamp(entryProto: any, type: TimestampType): undefined|Timestamp {
override getTimestamp(type: TimestampType, entryProto: any): undefined|Timestamp {
if (type !== TimestampType.ELAPSED) {
return undefined;
}

View File

@@ -28,6 +28,9 @@ class CommonTestUtils {
}
static getFixturePath(filename: string): string {
if (path.isAbsolute(filename)) {
return filename;
}
return path.join(CommonTestUtils.getProjectRootPath(), "src/test/fixtures", filename);
}

View File

@@ -24,7 +24,7 @@ describe("Viewer WindowManager", () => {
it("processes trace and renders view", () => {
const inputFile = element(by.css("input[type=\"file\"]"));
inputFile.sendKeys(E2eTestUtils.getFixturePath("trace_WindowManager.pb"));
inputFile.sendKeys(E2eTestUtils.getFixturePath("traces/elapsed_and_real_timestamp/WindowManager.pb"));
const windowManagerViewerTitle = element(by.css(".viewer-window-manager .title"));
expect(windowManagerViewerTitle.getText()).toContain("Window Manager");