Display real timestamp where possible
Fixes: 258782161
Test: Load traces and make sure timestamps displayed everywhere are real timestamps when available
Change-Id: I283232ab9308f452ff8fea2a55fc56af23a9b3ba
diff --git a/tools/winscope-ng/package-lock.json b/tools/winscope-ng/package-lock.json
index 1fb8606..d0f3f43 100644
--- a/tools/winscope-ng/package-lock.json
+++ b/tools/winscope-ng/package-lock.json
@@ -27,6 +27,7 @@
"@types/three": "^0.143.0",
"angular2-template-loader": "^0.6.2",
"auth0": "^2.42.0",
+ "dateformat": "^5.0.3",
"gl-matrix": "^3.4.3",
"html-loader": "^3.1.0",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
@@ -54,6 +55,7 @@
"@angular/cli": "~14.0.0",
"@angular/compiler-cli": "^14.0.0",
"@ngxs/devtools-plugin": "^3.7.4",
+ "@types/dateformat": "^5.0.0",
"@types/jasmine": "~4.0.0",
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.4",
@@ -3393,6 +3395,12 @@
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
"dev": true
},
+ "node_modules/@types/dateformat": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
+ "integrity": "sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==",
+ "dev": true
+ },
"node_modules/@types/eslint": {
"version": "8.4.5",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
@@ -5849,6 +5857,14 @@
"node": ">=4.0"
}
},
+ "node_modules/dateformat": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz",
+ "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -18135,6 +18151,12 @@
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
"dev": true
},
+ "@types/dateformat": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-5.0.0.tgz",
+ "integrity": "sha512-SZg4JdHIWHQGEokbYGZSDvo5wA4TLYPXaqhigs/wH+REDOejcJzgH+qyY+HtEUtWOZxEUkbhbdYPqQDiEgrXeA==",
+ "dev": true
+ },
"@types/eslint": {
"version": "8.4.5",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
@@ -20024,6 +20046,11 @@
"integrity": "sha512-VS20KRyorrbMCQmpdl2hg5KaOUsda1RbnsJg461FfrcyCUg+pkd0b40BSW4niQyTheww4DBXQnS7HwSrKkipLw==",
"dev": true
},
+ "dateformat": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz",
+ "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA=="
+ },
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
diff --git a/tools/winscope-ng/package.json b/tools/winscope-ng/package.json
index 570ab0e..c38fb46 100644
--- a/tools/winscope-ng/package.json
+++ b/tools/winscope-ng/package.json
@@ -36,6 +36,7 @@
"@types/three": "^0.143.0",
"angular2-template-loader": "^0.6.2",
"auth0": "^2.42.0",
+ "dateformat": "^5.0.3",
"gl-matrix": "^3.4.3",
"html-loader": "^3.1.0",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
@@ -63,6 +64,7 @@
"@angular/cli": "~14.0.0",
"@angular/compiler-cli": "^14.0.0",
"@ngxs/devtools-plugin": "^3.7.4",
+ "@types/dateformat": "^5.0.0",
"@types/jasmine": "~4.0.0",
"@types/jquery": "^3.5.14",
"@types/node": "^18.0.4",
diff --git a/tools/winscope-ng/src/app/app.module.ts b/tools/winscope-ng/src/app/app.module.ts
index 8b0a1c9..fbd8bff 100644
--- a/tools/winscope-ng/src/app/app.module.ts
+++ b/tools/winscope-ng/src/app/app.module.ts
@@ -73,6 +73,7 @@
import { ExpandedTimelineComponent } from "./components/timeline/expanded_timeline.component";
import { SingleTimelineComponent } from "./components/timeline/single_timeline.component";
import { MatDrawerContent, MatDrawer, MatDrawerContainer } from "./components/bottomnav/bottom_drawer.component";
+import { TimeUtils } from "common/utils/time_utils";
@NgModule({
declarations: [
diff --git a/tools/winscope-ng/src/app/components/timeline/timeline.component.ts b/tools/winscope-ng/src/app/components/timeline/timeline.component.ts
index 5f1bb5d..fa0372a 100644
--- a/tools/winscope-ng/src/app/components/timeline/timeline.component.ts
+++ b/tools/winscope-ng/src/app/components/timeline/timeline.component.ts
@@ -32,7 +32,7 @@
import { TRACE_INFO } from "app/trace_info";
import { TimelineCoordinator, TimestampChangeObserver } from "app/timeline_coordinator";
import { MiniTimelineComponent } from "./mini_timeline.component";
-import { Timestamp } from "common/trace/timestamp";
+import { Timestamp, TimestampType } from "common/trace/timestamp";
import { TimeUtils } from "common/utils/time_utils";
@Component({
@@ -69,10 +69,24 @@
<mat-icon>chevron_left</mat-icon>
</button>
<form [formGroup]="timestampForm" class="time-selector-form">
- <mat-form-field class="time-input" appearance="fill" (change)="inputTimeChanged($event)">
- <input matInput name="humanTimeInput" [formControl]="selectedTimeFormControl" />
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="humanElapsedTimeInputChange($event)"
+ *ngIf="!usingRealtime()">
+ <input matInput name="humanElapsedTimeInput" [formControl]="selectedElapsedTimeFormControl" />
</mat-form-field>
- <mat-form-field class="time-input" appearance="fill" (change)="inputTimeChanged($event)">
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="humanRealTimeInputChanged($event)"
+ *ngIf="usingRealtime()">
+ <input matInput name="humanRealTimeInput" [formControl]="selectedRealTimeFormControl" />
+ </mat-form-field>
+ <mat-form-field
+ class="time-input"
+ appearance="fill"
+ (change)="nanosecondsInputTimeChange($event)">
<input matInput name="nsTimeInput" [formControl]="selectedNsFormControl" />
</mat-form-field>
</form>
@@ -295,14 +309,18 @@
selectedTraces: TraceType[] = [];
selectedTracesFormControl = new FormControl();
- selectedTimeFormControl = new FormControl("", Validators.compose([
+ selectedElapsedTimeFormControl = new FormControl("undefined", Validators.compose([
Validators.required,
- Validators.pattern(TimeUtils.HUMAN_TIMESTAMP_REGEX)]));
- selectedNsFormControl = new FormControl(BigInt(0), Validators.compose([
+ Validators.pattern(TimeUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX)]));
+ selectedRealTimeFormControl = new FormControl("undefined", Validators.compose([
+ Validators.required,
+ Validators.pattern(TimeUtils.HUMAN_REAL_TIMESTAMP_REGEX)]));
+ selectedNsFormControl = new FormControl("undefined", Validators.compose([
Validators.required,
Validators.pattern(TimeUtils.NS_TIMESTAMP_REGEX)]));
timestampForm = new FormGroup({
- selectedTime: this.selectedTimeFormControl,
+ selectedElapsedTime: this.selectedElapsedTimeFormControl,
+ selectedRealTime: this.selectedRealTimeFormControl,
selectedNs: this.selectedNsFormControl,
});
@@ -375,14 +393,19 @@
this.timelineCoordinator.updateCurrentTimestamp(timestamp);
}
+ usingRealtime(): boolean {
+ return this.timelineCoordinator.getTimestampType() === TimestampType.REAL;
+ }
+
updateSeekTimestamp(timestamp: Timestamp|undefined) {
this.seekTimestamp = timestamp;
this.updateTimeInputValuesToCurrentTimestamp();
}
private updateTimeInputValuesToCurrentTimestamp() {
- this.selectedTimeFormControl.setValue(TimeUtils.nanosecondsToHuman(this.currentTimestamp.getValueNs(), false));
- this.selectedNsFormControl.setValue(this.currentTimestamp.getValueNs());
+ this.selectedElapsedTimeFormControl.setValue(TimeUtils.nanosecondsToHumanElapsed(this.currentTimestamp.getValueNs(), false));
+ this.selectedRealTimeFormControl.setValue(TimeUtils.nanosecondsToHumanReal(this.currentTimestamp.getValueNs()));
+ this.selectedNsFormControl.setValue(`${this.currentTimestamp.getValueNs()} ns`);
}
isOptionDisabled(trace: TraceType) {
@@ -451,26 +474,37 @@
this.timelineCoordinator.moveToNextEntryFor(this.wrappedActiveTrace);
}
- inputTimeChanged(event: Event) {
- console.error("Input time changed to", event);
+ humanElapsedTimeInputChange(event: Event) {
if (event.type !== "change") {
return;
}
-
const target = event.target as HTMLInputElement;
+ const timestamp = new Timestamp(this.timelineCoordinator.getTimestampType()!,
+ TimeUtils.humanElapsedToNanoseconds(target.value));
+ this.timelineCoordinator.updateCurrentTimestamp(timestamp);
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
- if (TimeUtils.NS_TIMESTAMP_REGEX.test(target.value)) {
- const timestamp = new Timestamp(this.timelineCoordinator.getTimestampType()!, BigInt(target.value));
- this.timelineCoordinator.updateCurrentTimestamp(timestamp);
- } else if (TimeUtils.HUMAN_TIMESTAMP_REGEX.test(target.value)) {
- const timestamp = new Timestamp(this.timelineCoordinator.getTimestampType()!,
- TimeUtils.humanToNanoseconds(target.value));
- this.timelineCoordinator.updateCurrentTimestamp(timestamp);
- } else {
- console.warn(`Invalid timestamp input provided "${target.value}" and can't be processed.`);
+ humanRealTimeInputChanged(event: Event) {
+ if (event.type !== "change") {
return;
}
+ const target = event.target as HTMLInputElement;
+ const timestamp = new Timestamp(this.timelineCoordinator.getTimestampType()!,
+ TimeUtils.humanRealToNanoseconds(target.value));
+ this.timelineCoordinator.updateCurrentTimestamp(timestamp);
+ this.updateTimeInputValuesToCurrentTimestamp();
+ }
+
+ nanosecondsInputTimeChange(event: Event) {
+ if (event.type !== "change") {
+ return;
+ }
+ const target = event.target as HTMLInputElement;
+
+ const timestamp = new Timestamp(this.timelineCoordinator.getTimestampType()!, BigInt(target.value));
+ this.timelineCoordinator.updateCurrentTimestamp(timestamp);
this.updateTimeInputValuesToCurrentTimestamp();
}
}
diff --git a/tools/winscope-ng/src/app/timeline_coordinator.ts b/tools/winscope-ng/src/app/timeline_coordinator.ts
index 3834e37..bced7d1 100644
--- a/tools/winscope-ng/src/app/timeline_coordinator.ts
+++ b/tools/winscope-ng/src/app/timeline_coordinator.ts
@@ -81,7 +81,7 @@
public getActiveTimestampForTraceAt(type: TraceType, timestamp: Timestamp): TimestampWithIndex|undefined {
if (timestamp.getType() !== this.timestampType) {
- throw Error("Invalid timestampt type");
+ throw Error("Invalid timestamp type");
}
const timeline = this.timelines.get(type);
diff --git a/tools/winscope-ng/src/common/trace/flickerlib/common.js b/tools/winscope-ng/src/common/trace/flickerlib/common.js
index d208120..af6376f 100644
--- a/tools/winscope-ng/src/common/trace/flickerlib/common.js
+++ b/tools/winscope-ng/src/common/trace/flickerlib/common.js
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import { TimeUtils } from 'common/utils/time_utils';
+
// Imports all the compiled common Flicker library classes and exports them
// as clean es6 modules rather than having them be commonjs modules
@@ -22,6 +24,8 @@
common.windowmanager.WindowManagerTrace;
const WindowManagerState = require('flicker').com.android.server.wm.traces.
common.windowmanager.WindowManagerState;
+const WindowManagerTraceEntryBuilder = require('flicker').com.android.server.wm.
+ traces.common.windowmanager.WindowManagerTraceEntryBuilder;
const Activity = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.Activity;
const Configuration = require('flicker').com.android.server.wm.traces.common.
@@ -277,6 +281,7 @@
WindowManagerPolicy,
WindowManagerTrace,
WindowManagerState,
+ WindowManagerTraceEntryBuilder,
// SF
BaseLayerTraceEntry,
Layer,
diff --git a/tools/winscope-ng/src/common/trace/flickerlib/layers/LayerTraceEntry.ts b/tools/winscope-ng/src/common/trace/flickerlib/layers/LayerTraceEntry.ts
index 9c56bba..592b7a3 100644
--- a/tools/winscope-ng/src/common/trace/flickerlib/layers/LayerTraceEntry.ts
+++ b/tools/winscope-ng/src/common/trace/flickerlib/layers/LayerTraceEntry.ts
@@ -17,20 +17,38 @@
import { Display, LayerTraceEntry, LayerTraceEntryBuilder, toRect, toSize, toTransform } from "../common";
import {Layer} from "./Layer";
import {getPropertiesForDisplay} from "../mixin";
+import { TimeUtils } from "common/utils/time_utils";
-LayerTraceEntry.fromProto = function (protos: any[], displayProtos: any[],
- timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
+LayerTraceEntry.fromProto = function (
+ protos: any[],
+ displayProtos: any[],
+ elapsedTimestamp: number,
+ vSyncId: number,
+ hwcBlob: string,
+ where: string = '',
+ realToElapsedTimeOffsetNs: bigint|undefined = undefined,
+ useElapsedTime = false
+): LayerTraceEntry {
const layers = protos.map(it => Layer.fromProto(it));
const displays = (displayProtos || []).map(it => newDisplay(it));
- const builder = new LayerTraceEntryBuilder(timestamp, layers, displays, hwcBlob, where);
+ const builder = new LayerTraceEntryBuilder(
+ `${elapsedTimestamp}`,
+ layers,
+ displays,
+ vSyncId,
+ hwcBlob,
+ where,
+ `${realToElapsedTimeOffsetNs ?? 0}`
+ );
const entry: LayerTraceEntry = builder.build();
- addAttributes(entry, protos);
+ addAttributes(entry, protos,
+ realToElapsedTimeOffsetNs === undefined || useElapsedTime);
return entry;
}
-function addAttributes(entry: LayerTraceEntry, protos: any) {
- entry.kind = "entry"
+function addAttributes(entry: LayerTraceEntry, protos: any, useElapsedTime = false) {
+ entry.kind = "entry";
// There no JVM/JS translation for Longs yet
entry.timestampMs = entry.timestamp.toString()
// Avoid parsing the entry root because it is an array of layers
@@ -43,7 +61,13 @@
if (newObj.physicalDisplayBounds) delete newObj.physicalDisplayBounds;
if (newObj.isVisible) delete newObj.isVisible;
entry.proto = newObj;
- entry.shortName = entry.name;
+ if (useElapsedTime || entry.clockTimestamp == undefined) {
+ entry.name = TimeUtils.nanosecondsToHumanElapsed(BigInt(entry.elapsedTimestamp));
+ entry.shortName = entry.name;
+ } else {
+ entry.name = TimeUtils.nanosecondsToHumanReal(BigInt(entry.clockTimestamp));
+ entry.shortName = entry.name;
+ }
entry.isVisible = true;
}
diff --git a/tools/winscope-ng/src/common/trace/flickerlib/windows/WindowManagerState.ts b/tools/winscope-ng/src/common/trace/flickerlib/windows/WindowManagerState.ts
index 02f5348..8b4044c 100644
--- a/tools/winscope-ng/src/common/trace/flickerlib/windows/WindowManagerState.ts
+++ b/tools/winscope-ng/src/common/trace/flickerlib/windows/WindowManagerState.ts
@@ -14,16 +14,24 @@
* limitations under the License.
*/
+import { TimeUtils } from "common/utils/time_utils";
import {
KeyguardControllerState,
RootWindowContainer,
WindowManagerPolicy,
- WindowManagerState
+ WindowManagerState,
+ WindowManagerTraceEntryBuilder
} from "../common"
import WindowContainer from "./WindowContainer"
-WindowManagerState.fromProto = function (proto: any, timestamp: number = 0, where: string = ""): WindowManagerState {
+WindowManagerState.fromProto = function (
+ proto: any,
+ elapsedTimestamp: number = 0,
+ where: string = "",
+ realToElapsedTimeOffsetNs: bigint|undefined = undefined,
+ useElapsedTime = false,
+): WindowManagerState {
var inputMethodWIndowAppToken = "";
if (proto.inputMethodWindow != null) {
proto.inputMethodWindow.hashCode.toString(16)
@@ -36,8 +44,8 @@
proto.rootWindowContainer.keyguardController);
const policy = createWindowManagerPolicy(proto.policy);
- const entry = new WindowManagerState(
- where,
+ const entry = new WindowManagerTraceEntryBuilder(
+ `${elapsedTimestamp}`,
policy,
proto.focusedApp,
proto.focusedDisplayId,
@@ -48,14 +56,15 @@
proto.rootWindowContainer.pendingActivities.map((it: any) => it.title),
rootWindowContainer,
keyguardControllerState,
- /*timestamp */ `${timestamp}`
- );
+ where,
+ `${realToElapsedTimeOffsetNs ?? 0}`,
+ ).build();
- addAttributes(entry, proto);
+ addAttributes(entry, proto, realToElapsedTimeOffsetNs === undefined || useElapsedTime);
return entry
}
-function addAttributes(entry: WindowManagerState, proto: any) {
+function addAttributes(entry: WindowManagerState, proto: any, useElapsedTime = false) {
entry.kind = entry.constructor.name;
// There no JVM/JS translation for Longs yet
entry.timestampMs = entry.timestamp.toString();
@@ -63,7 +72,13 @@
entry.isIncompleteReason = entry.getIsIncompleteReason();
}
entry.proto = proto;
- entry.shortName = entry.name;
+ if (useElapsedTime || entry.clockTimestamp == undefined) {
+ entry.name = TimeUtils.nanosecondsToHumanElapsed(BigInt(entry.elapsedTimestamp));
+ entry.shortName = entry.name;
+ } else {
+ entry.name = TimeUtils.nanosecondsToHumanReal(BigInt(entry.clockTimestamp));
+ entry.shortName = entry.name;
+ }
entry.isVisible = true;
}
diff --git a/tools/winscope-ng/src/common/trace/protolog.ts b/tools/winscope-ng/src/common/trace/protolog.ts
index a93528f..1b58d7b 100644
--- a/tools/winscope-ng/src/common/trace/protolog.ts
+++ b/tools/winscope-ng/src/common/trace/protolog.ts
@@ -15,6 +15,7 @@
*/
import {TimeUtils} from "common/utils/time_utils";
import configJson from "../../../../../../frameworks/base/data/etc/services.core.protolog.json";
+import { TimestampType } from "./timestamp";
class ProtoLogTraceEntry {
constructor(public messages: LogMessage[], public currentMessageIndex: number) {
@@ -27,9 +28,9 @@
tag: string;
level: string;
at: string;
- timestamp: number;
+ timestamp: bigint;
- constructor(text: string, time: string, tag: string, level: string, at: string, timestamp: number) {
+ constructor(text: string, time: string, tag: string, level: string, at: string, timestamp: bigint) {
this.text = text;
this.time = time;
this.tag = tag;
@@ -40,7 +41,7 @@
}
class FormattedLogMessage extends LogMessage {
- constructor(proto: any) {
+ constructor(proto: any, timestampType: TimestampType, realToElapsedTimeOffsetNs: bigint|undefined) {
const text = (
proto.messageHash.toString() +
" - [" + proto.strParams.toString() +
@@ -48,25 +49,46 @@
"] [" + proto.doubleParams.toString() +
"] [" + proto.booleanParams.toString() + "]"
);
+
+ let time: string;
+ let timestamp: bigint;
+ if (timestampType === TimestampType.REAL && realToElapsedTimeOffsetNs !== undefined && realToElapsedTimeOffsetNs != 0n) {
+ timestamp = realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.nanosecondsToHumanReal(realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos));
+ } else {
+ timestamp = BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.nanosecondsToHumanElapsed(timestamp);
+ }
+
super(
text,
- TimeUtils.nanosecondsToHuman(proto.elapsedRealtimeNanos),
+ time,
"INVALID",
"invalid",
"",
- Number(proto.elapsedRealtimeNanos));
+ timestamp);
}
}
class UnformattedLogMessage extends LogMessage {
- constructor(proto: any, message: any) {
+ constructor(proto: any, timestampType: TimestampType, realToElapsedTimeOffsetNs: bigint|undefined, message: any) {
+ let time: string;
+ let timestamp: bigint;
+ if (timestampType === TimestampType.REAL && realToElapsedTimeOffsetNs !== undefined && realToElapsedTimeOffsetNs != 0n) {
+ timestamp = realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.nanosecondsToHumanReal(realToElapsedTimeOffsetNs + BigInt(proto.elapsedRealtimeNanos));
+ } else {
+ timestamp = BigInt(proto.elapsedRealtimeNanos);
+ time = TimeUtils.nanosecondsToHumanElapsed(timestamp);
+ }
+
super(
formatText(message.message, proto),
- TimeUtils.nanosecondsToHuman(proto.elapsedRealtimeNanos),
+ time,
(<any>configJson).groups[message.group].tag,
message.level,
message.at,
- Number(proto.elapsedRealtimeNanos)
+ timestamp
);
}
}
diff --git a/tools/winscope-ng/src/common/trace/trace_tree_node.ts b/tools/winscope-ng/src/common/trace/trace_tree_node.ts
index 0836c85..cd8cefd 100644
--- a/tools/winscope-ng/src/common/trace/trace_tree_node.ts
+++ b/tools/winscope-ng/src/common/trace/trace_tree_node.ts
@@ -26,7 +26,8 @@
inputMethodService?: any;
inputMethodManagerService?: any;
where?: string;
- elapsedRealtimeNanos?: number;
+ elapsedRealtimeNanos?: number|bigint;
+ clockTimeNanos?: number|bigint;
shortName?: string;
type?: string;
id?: string | number;
diff --git a/tools/winscope-ng/src/common/trace/transactions.ts b/tools/winscope-ng/src/common/trace/transactions.ts
index c984077..d259c55 100644
--- a/tools/winscope-ng/src/common/trace/transactions.ts
+++ b/tools/winscope-ng/src/common/trace/transactions.ts
@@ -14,8 +14,15 @@
* limitations under the License.
*/
+import { TimestampType } from "./timestamp";
+
class TransactionsTraceEntry {
- constructor(public entriesProto: any[], public currentEntryIndex: number) {
+ constructor(
+ public entriesProto: any[],
+ public timestampType: TimestampType,
+ public realToElapsedTimeOffsetNs: bigint|undefined,
+ public currentEntryIndex: number,
+ ) {
}
}
diff --git a/tools/winscope-ng/src/common/utils/time_utils.spec.ts b/tools/winscope-ng/src/common/utils/time_utils.spec.ts
index face377..cb179a6 100644
--- a/tools/winscope-ng/src/common/utils/time_utils.spec.ts
+++ b/tools/winscope-ng/src/common/utils/time_utils.spec.ts
@@ -13,87 +13,143 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { TimestampType } from "common/trace/timestamp";
import {TimeUtils} from "./time_utils";
describe("TimeUtils", () => {
+ const MILLISECOND = BigInt(1000000);
+ const SECOND = BigInt(1000) * MILLISECOND;
+ const MINUTE = BigInt(60) * SECOND;
+ const HOUR = BigInt(60) * MINUTE;
+ const DAY = BigInt(24) * HOUR;
+
it("nanosecondsToHuman", () => {
- const MILLISECOND = 1000000;
- const SECOND = 1000 * MILLISECOND;
- const MINUTE = 60 * SECOND;
- const HOUR = 60 * MINUTE;
- const DAY = 24 * HOUR;
+ expect(TimeUtils.nanosecondsToHumanElapsed(0)) .toEqual("0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(0, false)) .toEqual("0ns");
+ expect(TimeUtils.nanosecondsToHumanElapsed(1000)) .toEqual("0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(1000, false)) .toEqual("1000ns");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MILLISECOND-1n)).toEqual("0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MILLISECOND)).toEqual("1ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(10n * MILLISECOND)).toEqual("10ms");
- expect(TimeUtils.nanosecondsToHuman(0)) .toEqual("0ms");
- expect(TimeUtils.nanosecondsToHuman(0, false)) .toEqual("0ns");
- expect(TimeUtils.nanosecondsToHuman(1000)) .toEqual("0ms");
- expect(TimeUtils.nanosecondsToHuman(1000, false)) .toEqual("1000ns");
- expect(TimeUtils.nanosecondsToHuman(MILLISECOND-1)).toEqual("0ms");
- expect(TimeUtils.nanosecondsToHuman(MILLISECOND)).toEqual("1ms");
- expect(TimeUtils.nanosecondsToHuman(10 * MILLISECOND)).toEqual("10ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(SECOND-1n)).toEqual("999ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(SECOND)).toEqual("1s0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(SECOND + MILLISECOND)).toEqual("1s1ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(SECOND + MILLISECOND, false)).toEqual("1s1ms0ns");
- expect(TimeUtils.nanosecondsToHuman(SECOND-1)).toEqual("999ms");
- expect(TimeUtils.nanosecondsToHuman(SECOND)).toEqual("1s0ms");
- expect(TimeUtils.nanosecondsToHuman(SECOND + MILLISECOND)).toEqual("1s1ms");
- expect(TimeUtils.nanosecondsToHuman(SECOND + MILLISECOND, false)).toEqual("1s1ms0ns");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MINUTE-1n)).toEqual("59s999ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MINUTE)).toEqual("1m0s0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MINUTE + SECOND + MILLISECOND)).toEqual("1m1s1ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MINUTE + SECOND + MILLISECOND + 1n)).toEqual("1m1s1ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(MINUTE + SECOND + MILLISECOND + 1n, false)).toEqual("1m1s1ms1ns");
- expect(TimeUtils.nanosecondsToHuman(MINUTE-1)).toEqual("59s999ms");
- expect(TimeUtils.nanosecondsToHuman(MINUTE)).toEqual("1m0s0ms");
- expect(TimeUtils.nanosecondsToHuman(MINUTE + SECOND + MILLISECOND)).toEqual("1m1s1ms");
- expect(TimeUtils.nanosecondsToHuman(MINUTE + SECOND + MILLISECOND + 1)).toEqual("1m1s1ms");
- expect(TimeUtils.nanosecondsToHuman(MINUTE + SECOND + MILLISECOND + 1, false)).toEqual("1m1s1ms1ns");
+ expect(TimeUtils.nanosecondsToHumanElapsed(HOUR-1n)).toEqual("59m59s999ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(HOUR-1n, false)).toEqual("59m59s999ms999999ns");
+ expect(TimeUtils.nanosecondsToHumanElapsed(HOUR)).toEqual("1h0m0s0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(HOUR + MINUTE + SECOND + MILLISECOND)).toEqual("1h1m1s1ms");
- expect(TimeUtils.nanosecondsToHuman(HOUR-1)).toEqual("59m59s999ms");
- expect(TimeUtils.nanosecondsToHuman(HOUR-1, false)).toEqual("59m59s999ms999999ns");
- expect(TimeUtils.nanosecondsToHuman(HOUR)).toEqual("1h0m0s0ms");
- expect(TimeUtils.nanosecondsToHuman(HOUR + MINUTE + SECOND + MILLISECOND)).toEqual("1h1m1s1ms");
-
- expect(TimeUtils.nanosecondsToHuman(DAY-1)).toEqual("23h59m59s999ms");
- expect(TimeUtils.nanosecondsToHuman(DAY)).toEqual("1d0h0m0s0ms");
- expect(TimeUtils.nanosecondsToHuman(DAY + HOUR + MINUTE + SECOND + MILLISECOND)).toEqual("1d1h1m1s1ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(DAY-1n)).toEqual("23h59m59s999ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(DAY)).toEqual("1d0h0m0s0ms");
+ expect(TimeUtils.nanosecondsToHumanElapsed(DAY + HOUR + MINUTE + SECOND + MILLISECOND)).toEqual("1d1h1m1s1ms");
});
- it("humanToNanoseconds", () => {
- const MILLISECOND = BigInt(1000000);
- const SECOND = BigInt(1000) * MILLISECOND;
- const MINUTE = BigInt(60) * SECOND;
- const HOUR = BigInt(60) * MINUTE;
- const DAY = BigInt(24) * HOUR;
+ it("humanElapsedToNanoseconds", () => {
+ expect(TimeUtils.humanElapsedToNanoseconds("0ns")) .toEqual(BigInt(0));
+ expect(TimeUtils.humanElapsedToNanoseconds("1000ns")) .toEqual(BigInt(1000));
+ expect(TimeUtils.humanElapsedToNanoseconds("0ms")).toEqual(BigInt(0));
+ expect(TimeUtils.humanElapsedToNanoseconds("1ms")).toEqual(MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("10ms")).toEqual(BigInt(10) * MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("0ns")) .toEqual(BigInt(0));
- expect(TimeUtils.humanToNanoseconds("1000ns")) .toEqual(BigInt(1000));
- expect(TimeUtils.humanToNanoseconds("0ms")).toEqual(BigInt(0));
- expect(TimeUtils.humanToNanoseconds("1ms")).toEqual(MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("10ms")).toEqual(BigInt(10) * MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("999ms")).toEqual(BigInt(999) * MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1s")).toEqual(SECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1s0ms")).toEqual(SECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1s0ms0ns")).toEqual(SECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1s0ms1ns")).toEqual(SECOND + BigInt(1));
+ expect(TimeUtils.humanElapsedToNanoseconds("0d1s1ms")).toEqual(SECOND + MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("999ms")).toEqual(BigInt(999) * MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("1s")).toEqual(SECOND);
- expect(TimeUtils.humanToNanoseconds("1s0ms")).toEqual(SECOND);
- expect(TimeUtils.humanToNanoseconds("1s0ms0ns")).toEqual(SECOND);
- expect(TimeUtils.humanToNanoseconds("1s0ms1ns")).toEqual(SECOND + BigInt(1));
- expect(TimeUtils.humanToNanoseconds("0d1s1ms")).toEqual(SECOND + MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1m0s0ms")).toEqual(MINUTE);
+ expect(TimeUtils.humanElapsedToNanoseconds("1m1s1ms")).toEqual(MINUTE + SECOND + MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("1m0s0ms")).toEqual(MINUTE);
- expect(TimeUtils.humanToNanoseconds("1m1s1ms")).toEqual(MINUTE + SECOND + MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1h0m")).toEqual(HOUR);
+ expect(TimeUtils.humanElapsedToNanoseconds("1h1m1s1ms")).toEqual(HOUR + MINUTE + SECOND + MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("1h0m")).toEqual(HOUR);
- expect(TimeUtils.humanToNanoseconds("1h1m1s1ms")).toEqual(HOUR + MINUTE + SECOND + MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1d0s1ms")).toEqual(DAY + MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1d1h1m1s1ms")).toEqual(DAY + HOUR + MINUTE + SECOND + MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("1d0s1ms")).toEqual(DAY + MILLISECOND);
- expect(TimeUtils.humanToNanoseconds("1d1h1m1s1ms")).toEqual(DAY + HOUR + MINUTE + SECOND + MILLISECOND);
-
- expect(TimeUtils.humanToNanoseconds("1d")).toEqual(DAY);
- expect(TimeUtils.humanToNanoseconds("1d1ms")).toEqual(DAY + MILLISECOND);
+ expect(TimeUtils.humanElapsedToNanoseconds("1d")).toEqual(DAY);
+ expect(TimeUtils.humanElapsedToNanoseconds("1d1ms")).toEqual(DAY + MILLISECOND);
});
it("humanToNanoseconds throws on invalid input format", () => {
- const invalidFormatError = new Error("Invalid timestamp format");
- expect(() => TimeUtils.humanToNanoseconds("1d1h1m1s0ns1ms") )
+ const invalidFormatError = new Error("Invalid elapsed timestamp format");
+ expect(() => TimeUtils.humanElapsedToNanoseconds("1d1h1m1s0ns1ms") )
.toThrow(invalidFormatError);
- expect(() => TimeUtils.humanToNanoseconds("1dns") )
+ expect(() => TimeUtils.humanElapsedToNanoseconds("1dns") )
.toThrow(invalidFormatError);
- expect(() => TimeUtils.humanToNanoseconds("100") )
+ expect(() => TimeUtils.humanElapsedToNanoseconds("100") )
.toThrow(invalidFormatError);
- expect(() => TimeUtils.humanToNanoseconds("") )
+ expect(() => TimeUtils.humanElapsedToNanoseconds("") )
.toThrow(invalidFormatError);
});
+
+ it("nanosecondsToHumanReal", () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(TimeUtils.nanosecondsToHumanReal(0))
+ .toEqual("00h00m00s000ms0ns, 1 Jan 1970 UTC");
+ expect(TimeUtils.nanosecondsToHumanReal(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186n * MILLISECOND + 123212n))
+ .toEqual("22h04m54s186ms123212ns, 10 Nov 2022 UTC");
+ expect(TimeUtils.nanosecondsToHumanReal(NOV_10_2022))
+ .toEqual("00h00m00s000ms0ns, 10 Nov 2022 UTC");
+ expect(TimeUtils.nanosecondsToHumanReal(NOV_10_2022 + 1n))
+ .toEqual("00h00m00s000ms1ns, 10 Nov 2022 UTC");
+ });
+
+ it("humanRealToNanoseconds", () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(TimeUtils.humanRealToNanoseconds("22h04m54s186ms123212ns, 10 Nov 2022 UTC"))
+ .toEqual(NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186n * MILLISECOND + 123212n);
+ expect(TimeUtils.humanRealToNanoseconds("22h04m54s186ms123212ns, 10 Nov 2022")).toEqual(1668117894186123212n);
+ expect(TimeUtils.humanRealToNanoseconds("22h04m54s186ms212ns, 10 Nov 2022 UTC")).toEqual(1668117894186000212n);
+ expect(TimeUtils.humanRealToNanoseconds("22h04m54s6ms2ns, 10 Nov 2022")).toEqual(1668117894006000002n);
+ expect(TimeUtils.humanRealToNanoseconds("06h4m54s6ms2ns, 10 Nov 2022")).toEqual(1668060294006000002n);
+ });
+
+ it("humanToNanoseconds throws on invalid input format", () => {
+ const invalidFormatError = new Error("Invalid real timestamp format");
+ expect(() => TimeUtils.humanRealToNanoseconds("23h59m59s999ms5ns") )
+ .toThrow(invalidFormatError);
+ expect(() => TimeUtils.humanRealToNanoseconds("1d") )
+ .toThrow(invalidFormatError);
+ expect(() => TimeUtils.humanRealToNanoseconds("100") )
+ .toThrow(invalidFormatError);
+ expect(() => TimeUtils.humanRealToNanoseconds("06h4m54s, 10 Nov 2022") )
+ .toThrow(invalidFormatError);
+ expect(() => TimeUtils.humanRealToNanoseconds("") )
+ .toThrow(invalidFormatError);
+ });
+
+ it("nano second regex accept all expected inputs", () => {
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("123")).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("123ns")).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("123 ns")).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test(" 123 ns ")).toBeTrue();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test(" 123 ")).toBeTrue();
+
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("1a23")).toBeFalse();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("a123 ns")).toBeFalse();
+ expect(TimeUtils.NS_TIMESTAMP_REGEX.test("")).toBeFalse();
+ });
+
+ it("format real", () => {
+ expect(TimeUtils.format(TimestampType.REAL, 100n, 500n)).toEqual("00h00m00s000ms600ns, 1 Jan 1970 UTC");
+ expect(() => {
+ TimeUtils.format(TimestampType.REAL, 100n);
+ }).toThrow(Error("clockTimeOffset required to format real timestamp"));
+ });
+
+ it("format elapsed", () => {
+ expect(TimeUtils.format(TimestampType.ELAPSED, 100n * MILLISECOND, 500n)).toEqual("100ms");
+ expect(TimeUtils.format(TimestampType.ELAPSED, 100n * MILLISECOND)).toEqual("100ms");
+ });
});
diff --git a/tools/winscope-ng/src/common/utils/time_utils.ts b/tools/winscope-ng/src/common/utils/time_utils.ts
index f3ee6b4..ba98ce0 100644
--- a/tools/winscope-ng/src/common/utils/time_utils.ts
+++ b/tools/winscope-ng/src/common/utils/time_utils.ts
@@ -13,8 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+import { TimestampType } from "common/trace/timestamp";
+import dateFormat, { masks } from "dateformat";
+
export class TimeUtils {
- static nanosecondsToHuman(timestampNanos: number|bigint, hideNs = true): string {
+ static format(timestampType: TimestampType,
+ elapsedTime: bigint, clockTimeOffset: bigint|undefined = undefined): string {
+ switch (timestampType) {
+ case TimestampType.ELAPSED: {
+ return TimeUtils.nanosecondsToHumanElapsed(elapsedTime);
+ }
+ case TimestampType.REAL: {
+ if (clockTimeOffset === undefined) {
+ throw Error("clockTimeOffset required to format real timestamp");
+ }
+ return TimeUtils.nanosecondsToHumanReal(elapsedTime + clockTimeOffset);
+ }
+ default: {
+ throw Error("Unhandled timestamp type");
+ }
+ }
+ }
+
+ static nanosecondsToHumanElapsed(timestampNanos: number|bigint, hideNs = true): string {
timestampNanos = BigInt(timestampNanos);
const units = TimeUtils.units;
@@ -40,9 +62,19 @@
return parts.join("");
}
- static humanToNanoseconds(timestampHuman: string): bigint {
- if (!TimeUtils.HUMAN_TIMESTAMP_REGEX.test(timestampHuman)) {
- throw Error("Invalid timestamp format");
+ static nanosecondsToHumanReal(timestampNanos: number|bigint): string {
+ timestampNanos = BigInt(timestampNanos);
+ const ms = timestampNanos / 1000000n;
+ const extraNanos = timestampNanos % 1000000n;
+ const formattedTime = dateFormat(new Date(Number(ms)), "HH\"h\"MM\"m\"ss\"s\"l\"ms\"");
+ const formattedDate = dateFormat(new Date(Number(ms)), "d mmm yyyy Z");
+
+ return `${formattedTime}${extraNanos}ns, ${formattedDate}`;
+ }
+
+ static humanElapsedToNanoseconds(timestampHuman: string): bigint {
+ if (!TimeUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX.test(timestampHuman)) {
+ throw Error("Invalid elapsed timestamp format");
}
const units = TimeUtils.units;
@@ -62,16 +94,55 @@
return ns;
}
+ static humanRealToNanoseconds(timestampHuman: string): bigint {
+ if (!TimeUtils.HUMAN_REAL_TIMESTAMP_REGEX.test(timestampHuman)) {
+ throw Error("Invalid real timestamp format");
+ }
+
+ const time = timestampHuman.split(",")[0];
+ const date = timestampHuman.split(",")[1];
+
+ let timeRest = time;
+ const hours = parseInt(timeRest.split("h")[0]);
+ timeRest = time.split("h")[1];
+ const minutes = parseInt(timeRest.split("m")[0]);
+ timeRest = time.split("m")[1];
+ const seconds = parseInt(timeRest.split("s")[0]);
+ timeRest = time.split("s")[1];
+ const milliseconds = parseInt(timeRest.split("ms")[0]);
+ timeRest = time.split("ms")[1];
+ const nanoseconds = parseInt(timeRest);
+
+ const dateMilliseconds = new Date(date).getTime();
+
+ return BigInt(hours) * BigInt(TimeUtils.TO_NANO["h"]) +
+ BigInt(minutes) * BigInt(TimeUtils.TO_NANO["m"]) +
+ BigInt(seconds) * BigInt(TimeUtils.TO_NANO["s"]) +
+ BigInt(milliseconds) * BigInt(TimeUtils.TO_NANO["ms"]) +
+ BigInt(nanoseconds) * BigInt(TimeUtils.TO_NANO["ns"]) +
+ BigInt(dateMilliseconds) * BigInt(TimeUtils.TO_NANO["ms"]);
+ }
+
+ static TO_NANO = {
+ "ns": 1,
+ "ms": 1000000,
+ "s": 1000000 * 1000,
+ "m": 1000000 * 1000 * 60,
+ "h": 1000000 * 1000 * 60 * 60,
+ "d": 1000000 * 1000 * 60 * 60 * 24
+ };
+
static units = [
- {nanosInUnit: 1, unit: "ns"},
- {nanosInUnit: 1000000, unit: "ms"},
- {nanosInUnit: 1000000 * 1000, unit: "s"},
- {nanosInUnit: 1000000 * 1000 * 60, unit: "m"},
- {nanosInUnit: 1000000 * 1000 * 60 * 60, unit: "h"},
- {nanosInUnit: 1000000 * 1000 * 60 * 60 * 24, unit: "d"},
+ {nanosInUnit: TimeUtils.TO_NANO["ns"], unit: "ns"},
+ {nanosInUnit: TimeUtils.TO_NANO["ms"], unit: "ms"},
+ {nanosInUnit: TimeUtils.TO_NANO["s"], unit: "s"},
+ {nanosInUnit: TimeUtils.TO_NANO["m"], unit: "m"},
+ {nanosInUnit: TimeUtils.TO_NANO["h"], unit: "h"},
+ {nanosInUnit: TimeUtils.TO_NANO["d"], unit: "d"},
];
// (?=.) checks there is at least one character with a lookahead match
- static readonly HUMAN_TIMESTAMP_REGEX = /^(?=.)([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?([0-9]+ns)?$/;
- static readonly NS_TIMESTAMP_REGEX = /^[0-9]+$/;
+ static readonly HUMAN_ELAPSED_TIMESTAMP_REGEX = /^(?=.)([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?([0-9]+ns)?$/;
+ static readonly HUMAN_REAL_TIMESTAMP_REGEX = /^[0-9]([0-9])?h[0-9]([0-9])?m[0-9]([0-9])?s[0-9]([0-9])?([0-9])?ms[0-9]([0-9])?([0-9])?([0-9])?([0-9])?([0-9])?ns, [0-9]([0-9])? [A-Za-z][A-Za-z][A-Za-z] [0-9][0-9][0-9][0-9]( [A-Za-z][A-Za-z][A-Za-z])?$/;
+ static readonly NS_TIMESTAMP_REGEX = /^\s*[0-9]+(\s?ns)?\s*$/;
}
diff --git a/tools/winscope-ng/src/parsers/parser.ts b/tools/winscope-ng/src/parsers/parser.ts
index 596b497..c14b517 100644
--- a/tools/winscope-ng/src/parsers/parser.ts
+++ b/tools/winscope-ng/src/parsers/parser.ts
@@ -78,13 +78,13 @@
if (index === undefined) {
return undefined;
}
- return this.processDecodedEntry(index, this.decodedEntries[index]);
+ return this.processDecodedEntry(index, timestamp.getType(), this.decodedEntries[index]);
}
protected abstract getMagicNumber(): undefined|number[];
protected abstract decodeTrace(trace: Uint8Array): any[];
protected abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined|Timestamp;
- protected abstract processDecodedEntry(index: number, decodedEntry: any): any;
+ protected abstract processDecodedEntry(index: number, timestampType: TimestampType, decodedEntry: any): any;
protected trace: File;
protected decodedEntries: any[] = [];
diff --git a/tools/winscope-ng/src/parsers/parser_accessibility.ts b/tools/winscope-ng/src/parsers/parser_accessibility.ts
index f74895e..520fc07 100644
--- a/tools/winscope-ng/src/parsers/parser_accessibility.ts
+++ b/tools/winscope-ng/src/parsers/parser_accessibility.ts
@@ -53,7 +53,7 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: any): any {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): any {
return entryProto;
}
diff --git a/tools/winscope-ng/src/parsers/parser_common.spec.ts b/tools/winscope-ng/src/parsers/parser_common.spec.ts
index 530ce02..a9d00f8 100644
--- a/tools/winscope-ng/src/parsers/parser_common.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_common.spec.ts
@@ -67,19 +67,19 @@
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089075566202n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
- .toEqual(14474594000n);
+ .toEqual(1659107089075566202n);
});
it("retrieves trace entry (equal timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
- .toEqual(15398076788n);
+ .toEqual(1659107089999048990n);
});
it("retrieves trace entry (lower timestamp matches)", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048991n);
expect(BigInt(parser.getTraceEntry(timestamp)!.timestampMs))
- .toEqual(15398076788n);
+ .toEqual(1659107089999048990n);
});
});
diff --git a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts
index 0c8056c..e3743a7 100644
--- a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts
+++ b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts
@@ -57,9 +57,19 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: TraceTreeNode): TraceTreeNode {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: TraceTreeNode): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error("Missing elapsedRealtimeNanos on entry");
+ }
+
+ let clockTimeNanos: bigint|undefined = undefined;
+ if (this.realToElapsedTimeOffsetNs !== undefined
+ && entryProto.elapsedRealtimeNanos !== undefined) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
return {
- name: TimeUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
+ name: TimeUtils.format(timestampType, BigInt(entryProto.elapsedRealtimeNanos), this.realToElapsedTimeOffsetNs) + " - " + entryProto.where,
kind: "InputMethodClient entry",
children: [
{
@@ -75,6 +85,7 @@
stableId: "entry",
id: "entry",
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
};
}
diff --git a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts
index 0ab0269..d56ef08 100644
--- a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts
+++ b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts
@@ -55,9 +55,19 @@
return undefined;
}
- protected override processDecodedEntry(index: number, entryProto: TraceTreeNode): TraceTreeNode {
+ protected override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: TraceTreeNode): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error("Missing elapsedRealtimeNanos on entry");
+ }
+
+ let clockTimeNanos: bigint|undefined = undefined;
+ if (this.realToElapsedTimeOffsetNs !== undefined
+ && entryProto.elapsedRealtimeNanos !== undefined) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
return {
- name: TimeUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
+ name: TimeUtils.format(timestampType, BigInt(entryProto.elapsedRealtimeNanos), this.realToElapsedTimeOffsetNs) + " - " + entryProto.where,
kind: "InputMethodManagerService entry",
children: [
{
@@ -73,6 +83,7 @@
stableId: "entry",
id: "entry",
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
};
}
diff --git a/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts b/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts
index d159197..3d112f0 100644
--- a/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts
@@ -52,7 +52,7 @@
.toEqual(16578752896n);
});
- it("retrieves trace entry from elapsed timestamp", () => {
+ it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107091180519857n);
expect(BigInt(parser.getTraceEntry(timestamp)!.elapsedRealtimeNanos))
.toEqual(16578752896n);
diff --git a/tools/winscope-ng/src/parsers/parser_input_method_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_service.ts
index 9940ca4..4ef26f9 100644
--- a/tools/winscope-ng/src/parsers/parser_input_method_service.ts
+++ b/tools/winscope-ng/src/parsers/parser_input_method_service.ts
@@ -56,9 +56,19 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: TraceTreeNode): TraceTreeNode {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: TraceTreeNode): TraceTreeNode {
+ if (entryProto.elapsedRealtimeNanos === undefined) {
+ throw Error("Missing elapsedRealtimeNanos on entry");
+ }
+
+ let clockTimeNanos: bigint|undefined = undefined;
+ if (this.realToElapsedTimeOffsetNs !== undefined
+ && entryProto.elapsedRealtimeNanos !== undefined) {
+ clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs;
+ }
+
return {
- name: TimeUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
+ name: TimeUtils.format(timestampType, BigInt(entryProto.elapsedRealtimeNanos), this.realToElapsedTimeOffsetNs) + " - " + entryProto.where,
kind: "InputMethodService entry",
children: [
{
@@ -74,6 +84,7 @@
stableId: "entry",
id: "entry",
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
+ clockTimeNanos,
};
}
diff --git a/tools/winscope-ng/src/parsers/parser_protolog.spec.ts b/tools/winscope-ng/src/parsers/parser_protolog.spec.ts
index 3802465..1117d9c 100644
--- a/tools/winscope-ng/src/parsers/parser_protolog.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_protolog.spec.ts
@@ -22,13 +22,22 @@
describe("ParserProtoLog", () => {
let parser: Parser;
- const expectedFirstLogMessage = {
+ const expectedFirstLogMessageElapsed = {
text: "InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false",
time: "14m10s746ms",
tag: "WindowManager",
level: "DEBUG",
at: "com/android/server/wm/InsetsSourceProvider.java",
- timestamp: Number(850746266486),
+ timestamp: 850746266486n,
+ };
+
+ const expectedFirstLogMessageReal = {
+ text: "InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false",
+ time: "12h12m05s377ms266486ns, 20 Jun 2022 UTC",
+ tag: "WindowManager",
+ level: "DEBUG",
+ at: "com/android/server/wm/InsetsSourceProvider.java",
+ timestamp: 1655727125377266486n,
};
beforeAll(async () => {
@@ -67,14 +76,27 @@
.toEqual(expected);
});
- it("reconstructs human-readable log message", () => {
+ it("reconstructs human-readable log message (ELAPSED time)", () => {
const timestamp = new Timestamp(TimestampType.ELAPSED, 850746266486n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry.currentMessageIndex).toEqual(0);
expect(entry.messages.length).toEqual(50);
- expect(Object.assign({}, entry.messages[0])).toEqual(expectedFirstLogMessage);
+ expect(Object.assign({}, entry.messages[0])).toEqual(expectedFirstLogMessageElapsed);
+ entry.messages.forEach((message: any) => {
+ expect(message).toBeInstanceOf(LogMessage);
+ });
+ });
+
+ it("reconstructs human-readable log message (REAL time)", () => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1655727125377266486n);
+ const entry = parser.getTraceEntry(timestamp)!;
+
+ expect(entry.currentMessageIndex).toEqual(0);
+
+ expect(entry.messages.length).toEqual(50);
+ expect(Object.assign({}, entry.messages[0])).toEqual(expectedFirstLogMessageReal);
entry.messages.forEach((message: any) => {
expect(message).toBeInstanceOf(LogMessage);
});
diff --git a/tools/winscope-ng/src/parsers/parser_protolog.ts b/tools/winscope-ng/src/parsers/parser_protolog.ts
index 9e57eb7..5b18d56 100644
--- a/tools/winscope-ng/src/parsers/parser_protolog.ts
+++ b/tools/winscope-ng/src/parsers/parser_protolog.ts
@@ -61,40 +61,42 @@
if (type == TimestampType.ELAPSED) {
return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos));
}
- else if (type == TimestampType.REAL) {
- return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs!);
+ if (type == TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) {
+ return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs);
}
return undefined;
}
- override processDecodedEntry(index: number, entryProto: any): ProtoLogTraceEntry {
- if (!this.decodedMessages) {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): ProtoLogTraceEntry {
+ if (!this.decodedMessages || this.decodedTimestampType !== timestampType) {
+ this.decodedTimestampType = timestampType;
this.decodedMessages = this.decodedEntries.map((entryProto: any) => {
- return this.decodeProtoLogMessage(entryProto);
+ return this.decodeProtoLogMessage(entryProto, timestampType);
});
}
return new ProtoLogTraceEntry(this.decodedMessages, index);
}
- private decodeProtoLogMessage(entryProto: any): LogMessage {
+ private decodeProtoLogMessage(entryProto: any, timestampType: TimestampType): LogMessage {
const message = (<any>configJson).messages[entryProto.messageHash];
if (!message) {
- return new FormattedLogMessage(entryProto);
+ return new FormattedLogMessage(entryProto, timestampType, this.realToElapsedTimeOffsetNs);
}
try {
- return new UnformattedLogMessage(entryProto, message);
+ return new UnformattedLogMessage(entryProto, timestampType, this.realToElapsedTimeOffsetNs, message);
}
catch (error) {
if (error instanceof FormatStringMismatchError) {
- return new FormattedLogMessage(entryProto);
+ return new FormattedLogMessage(entryProto, timestampType, this.realToElapsedTimeOffsetNs);
}
throw error;
}
}
private decodedMessages?: LogMessage[];
+ private decodedTimestampType?: TimestampType;
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";
diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording.ts b/tools/winscope-ng/src/parsers/parser_screen_recording.ts
index 54e776a..c8c2352 100644
--- a/tools/winscope-ng/src/parsers/parser_screen_recording.ts
+++ b/tools/winscope-ng/src/parsers/parser_screen_recording.ts
@@ -81,7 +81,7 @@
return undefined;
}
- override processDecodedEntry(index: number, entry: ScreenRecordingMetadataEntry): ScreenRecordingTraceEntry {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entry: ScreenRecordingMetadataEntry): ScreenRecordingTraceEntry {
const initialTimestampNs = this.getTimestamps(TimestampType.ELAPSED)![0].getValueNs();
const currentTimestampNs = entry.timestampElapsedNs;
const videoTimeSeconds = Number(currentTimestampNs - initialTimestampNs) / 1000000000;
diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts
index 789ee7e..61487ca 100644
--- a/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts
+++ b/tools/winscope-ng/src/parsers/parser_screen_recording_legacy.ts
@@ -45,7 +45,7 @@
return decodedEntry;
}
- override processDecodedEntry(index: number, entry: Timestamp): ScreenRecordingTraceEntry {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entry: Timestamp): ScreenRecordingTraceEntry {
const currentTimestamp = entry;
const initialTimestamp = this.getTimestamps(TimestampType.ELAPSED)![0];
const videoTimeSeconds =
diff --git a/tools/winscope-ng/src/parsers/parser_surface_flinger.spec.ts b/tools/winscope-ng/src/parsers/parser_surface_flinger.spec.ts
index c5e1ee7..b3c8281 100644
--- a/tools/winscope-ng/src/parsers/parser_surface_flinger.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_surface_flinger.spec.ts
@@ -82,14 +82,20 @@
const timestamp = new Timestamp(TimestampType.ELAPSED, 14631249355n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
- expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
+ expect(BigInt(entry.timestampMs)).toEqual(1659107089233029344n);
});
- it("retrieves trace entry from elapsed timestamp", () => {
+ it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 1659107089233029344n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
- expect(BigInt(entry.timestampMs)).toEqual(14631249355n);
+ expect(BigInt(entry.timestampMs)).toEqual(1659107089233029344n);
+ });
+
+ it("formats entry timestamps", () => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659107089233029344n);
+ const entry = parser.getTraceEntry(timestamp)!;
+ expect(entry.name).toEqual("15h04m49s233ms29376ns, 29 Jul 2022 UTC");
});
});
@@ -113,5 +119,16 @@
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
+
+ it("formats entry timestamps", () => {
+ expect(() => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659107089233029344n);
+ parser.getTraceEntry(timestamp);
+ }).toThrow(Error("Timestamps with type \"REAL\" not available"));
+
+ const timestamp = new Timestamp(TimestampType.ELAPSED, 850335483446n);
+ const entry = parser.getTraceEntry(timestamp)!;
+ expect(entry.name).toEqual("14m10s335ms");
+ });
});
});
diff --git a/tools/winscope-ng/src/parsers/parser_surface_flinger.ts b/tools/winscope-ng/src/parsers/parser_surface_flinger.ts
index 6a6e163..5077ec3 100644
--- a/tools/winscope-ng/src/parsers/parser_surface_flinger.ts
+++ b/tools/winscope-ng/src/parsers/parser_surface_flinger.ts
@@ -59,8 +59,17 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: any): LayerTraceEntry {
- return LayerTraceEntry.fromProto(entryProto.layers.layers, entryProto.displays, entryProto.elapsedRealtimeNanos, entryProto.hwcBlob);
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): LayerTraceEntry {
+ return LayerTraceEntry.fromProto(
+ entryProto.layers.layers,
+ entryProto.displays,
+ entryProto.elapsedRealtimeNanos,
+ entryProto.vsyncId,
+ entryProto.hwcBlob,
+ entryProto.where,
+ this.realToElapsedTimeOffsetNs,
+ timestampType === TimestampType.ELAPSED /*useElapsedTime*/
+ );
}
private realToElapsedTimeOffsetNs: undefined|bigint;
diff --git a/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts b/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts
index e32da20..f3486ef 100644
--- a/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts
@@ -22,6 +22,7 @@
describe("ParserSurfaceFlingerDump", () => {
describe("trace with elapsed + real timestamp", () => {
let parser: Parser;
+ const DUMP_REAL_TIME = 1659176624505188647n;
beforeAll(async () => {
parser = await UnitTestUtils.getParser("traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb");
@@ -49,14 +50,14 @@
const timestamp = new Timestamp(TimestampType.ELAPSED, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
- expect(BigInt(entry.timestampMs)).toEqual(0n);
+ expect(BigInt(entry.timestampMs)).toEqual(DUMP_REAL_TIME);
});
- it("retrieves trace entry from elapsed timestamp", () => {
+ it("retrieves trace entry from real timestamp", () => {
const timestamp = new Timestamp(TimestampType.REAL, 0n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(LayerTraceEntry);
- expect(BigInt(entry.timestampMs)).toEqual(0n);
+ expect(BigInt(entry.timestampMs)).toEqual(DUMP_REAL_TIME);
});
});
diff --git a/tools/winscope-ng/src/parsers/parser_transactions.spec.ts b/tools/winscope-ng/src/parsers/parser_transactions.spec.ts
index 183bbcd..0994ffb 100644
--- a/tools/winscope-ng/src/parsers/parser_transactions.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_transactions.spec.ts
@@ -88,6 +88,14 @@
expect(entry.entriesProto[222].transactions[1].displayChanges[0].what)
.toEqual("eLayerStackChanged | eDisplayProjectionChanged | eFlagsChanged");
});
+
+ it("includes timestamp type in TransactionsTraceEntry", () => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659507541118452067n);
+ const entry: TransactionsTraceEntry = parser.getTraceEntry(timestamp)!;
+
+ expect(entry.timestampType).toEqual(TimestampType.REAL);
+ expect(entry.realToElapsedTimeOffsetNs).toEqual(1659507538600499552n);
+ });
});
describe("trace with elapsed (only) timestamp", () => {
@@ -120,5 +128,12 @@
expect(parser.getTimestamps(TimestampType.REAL))
.toEqual(undefined);
});
+
+ it("includes timestamp type in TransactionsTraceEntry", () => {
+ const timestamp = new Timestamp(TimestampType.ELAPSED, 14884850511n);
+ const entry: TransactionsTraceEntry = parser.getTraceEntry(timestamp)!;
+
+ expect(entry.timestampType).toEqual(TimestampType.ELAPSED);
+ });
});
});
diff --git a/tools/winscope-ng/src/parsers/parser_transactions.ts b/tools/winscope-ng/src/parsers/parser_transactions.ts
index f464f77..9eda543 100644
--- a/tools/winscope-ng/src/parsers/parser_transactions.ts
+++ b/tools/winscope-ng/src/parsers/parser_transactions.ts
@@ -100,8 +100,8 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: any): TransactionsTraceEntry {
- return new TransactionsTraceEntry(this.decodedEntries, index);
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): TransactionsTraceEntry {
+ return new TransactionsTraceEntry(this.decodedEntries, timestampType, this.realToElapsedTimeOffsetNs, index);
}
private realToElapsedTimeOffsetNs: undefined|bigint;
diff --git a/tools/winscope-ng/src/parsers/parser_window_manager.spec.ts b/tools/winscope-ng/src/parsers/parser_window_manager.spec.ts
index 3c50fc0..9230670 100644
--- a/tools/winscope-ng/src/parsers/parser_window_manager.spec.ts
+++ b/tools/winscope-ng/src/parsers/parser_window_manager.spec.ts
@@ -55,14 +55,20 @@
const timestamp = new Timestamp(TimestampType.ELAPSED, 15398076788n);
const entry = parser.getTraceEntry(timestamp)!;
expect(entry).toBeInstanceOf(WindowManagerState);
- expect(BigInt(entry.timestampMs)).toEqual(15398076788n);
+ expect(BigInt(entry.timestampMs)).toEqual(1659107089999048990n);
});
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);
+ expect(BigInt(entry.timestampMs)).toEqual(1659107089999048990n);
+ });
+
+ it("formats entry timestamps", () => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
+ const entry = parser.getTraceEntry(timestamp)!;
+ expect(entry.name).toEqual("15h04m49s999ms48960ns, 29 Jul 2022 UTC");
});
});
@@ -93,5 +99,16 @@
expect(entry).toBeInstanceOf(WindowManagerState);
expect(BigInt(entry.timestampMs)).toEqual(850254319343n);
});
+
+ it("formats entry timestamps", () => {
+ expect(() => {
+ const timestamp = new Timestamp(TimestampType.REAL, 1659107089999048990n);
+ parser.getTraceEntry(timestamp);
+ }).toThrow(Error("Timestamps with type \"REAL\" not available"));
+
+ const timestamp = new Timestamp(TimestampType.ELAPSED, 850254319343n);
+ const entry = parser.getTraceEntry(timestamp)!;
+ expect(entry.name).toEqual("14m10s254ms");
+ });
});
});
diff --git a/tools/winscope-ng/src/parsers/parser_window_manager.ts b/tools/winscope-ng/src/parsers/parser_window_manager.ts
index 5a38f72..b719db2 100644
--- a/tools/winscope-ng/src/parsers/parser_window_manager.ts
+++ b/tools/winscope-ng/src/parsers/parser_window_manager.ts
@@ -54,8 +54,14 @@
return undefined;
}
- override processDecodedEntry(index: number, entryProto: any): WindowManagerState {
- return WindowManagerState.fromProto(entryProto.windowManagerService, entryProto.elapsedRealtimeNanos, entryProto.where);
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): WindowManagerState {
+ return WindowManagerState.fromProto(
+ entryProto.windowManagerService,
+ entryProto.elapsedRealtimeNanos,
+ entryProto.where,
+ this.realToElapsedTimeOffsetNs,
+ timestampType === TimestampType.ELAPSED /*useElapsedTime*/
+ );
}
private realToElapsedTimeOffsetNs: undefined|bigint;
diff --git a/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts b/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts
index f929de2..8bc8075 100644
--- a/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts
+++ b/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts
@@ -40,7 +40,7 @@
// sure that a trace entry can actually be created from the decoded proto.
// If the trace entry creation fails, an exception is thrown and the parser
// will be considered unsuited for this input data.
- this.processDecodedEntry(0, entryProto);
+ this.processDecodedEntry(0, TimestampType.ELAPSED /*irrelevant for dump*/, entryProto);
return [entryProto];
}
@@ -52,7 +52,7 @@
return new Timestamp(TimestampType.ELAPSED, 0n);
}
- override processDecodedEntry(index: number, entryProto: any): WindowManagerState {
+ override processDecodedEntry(index: number, timestampType: TimestampType, entryProto: any): WindowManagerState {
return WindowManagerState.fromProto(entryProto);
}
}
diff --git a/tools/winscope-ng/src/viewers/viewer_protolog/presenter.spec.ts b/tools/winscope-ng/src/viewers/viewer_protolog/presenter.spec.ts
index 248c2fe..506b445 100644
--- a/tools/winscope-ng/src/viewers/viewer_protolog/presenter.spec.ts
+++ b/tools/winscope-ng/src/viewers/viewer_protolog/presenter.spec.ts
@@ -26,9 +26,9 @@
beforeEach(async () => {
inputMessages = [
- new LogMessage("text0", "time", "tag0", "level0", "sourcefile0", 10),
- new LogMessage("text1", "time", "tag1", "level1", "sourcefile1", 10),
- new LogMessage("text2", "time", "tag2", "level2", "sourcefile2", 10),
+ new LogMessage("text0", "time", "tag0", "level0", "sourcefile0", 10n),
+ new LogMessage("text1", "time", "tag1", "level1", "sourcefile1", 10n),
+ new LogMessage("text2", "time", "tag2", "level2", "sourcefile2", 10n),
];
inputTraceEntries = new Map<TraceType, any>();
inputTraceEntries.set(TraceType.PROTO_LOG, [new ProtoLogTraceEntry(inputMessages, 0)]);
diff --git a/tools/winscope-ng/src/viewers/viewer_transactions/presenter.spec.ts b/tools/winscope-ng/src/viewers/viewer_transactions/presenter.spec.ts
index 91c755d..45352a8 100644
--- a/tools/winscope-ng/src/viewers/viewer_transactions/presenter.spec.ts
+++ b/tools/winscope-ng/src/viewers/viewer_transactions/presenter.spec.ts
@@ -24,8 +24,10 @@
describe("ViewerTransactionsPresenter", () => {
let parser: Parser;
let presenter: Presenter;
- let inputTraceEntry: TransactionsTraceEntry;
- let inputTraceEntries: Map<TraceType, any>;
+ let inputTraceEntryElapsed: TransactionsTraceEntry;
+ let inputTraceEntriesElapsed: Map<TraceType, any>;
+ let inputTraceEntryReal: TransactionsTraceEntry;
+ let inputTraceEntriesReal: Map<TraceType, any>;
let outputUiData: undefined|UiData;
const TOTAL_OUTPUT_ENTRIES = 1504;
@@ -34,10 +36,16 @@
});
beforeEach(() => {
- const timestamp = new Timestamp(TimestampType.ELAPSED, 2450981445n);
- inputTraceEntry = parser.getTraceEntry(timestamp)!;
- inputTraceEntries = new Map<TraceType, any>();
- inputTraceEntries.set(TraceType.TRANSACTIONS, [inputTraceEntry]);
+ const elapsedTimestamp = new Timestamp(TimestampType.ELAPSED, 2450981445n);
+ inputTraceEntryElapsed = parser.getTraceEntry(elapsedTimestamp)!;
+ inputTraceEntriesElapsed = new Map<TraceType, any>();
+ inputTraceEntriesElapsed.set(TraceType.TRANSACTIONS, [inputTraceEntryElapsed]);
+
+ const realTimestamp = new Timestamp(TimestampType.REAL, 16595075386004995520n);
+ inputTraceEntryReal = parser.getTraceEntry(realTimestamp)!;
+ inputTraceEntriesReal = new Map<TraceType, any>();
+ inputTraceEntriesReal.set(TraceType.TRANSACTIONS, [inputTraceEntryReal]);
+
outputUiData = undefined;
presenter = new Presenter((data: UiData) => {
@@ -46,13 +54,13 @@
});
it("is robust to undefined trace entry", () => {
- inputTraceEntries = new Map<TraceType, any>();
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ inputTraceEntriesElapsed = new Map<TraceType, any>();
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData).toEqual(UiData.EMPTY);
});
it("processes trace entry and computes output UI data", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.allPids).toEqual(["N/A", "0", "515", "1593", "2022", "2322", "2463", "3300"]);
expect(outputUiData!.allUids).toEqual(["N/A", "1000", "1003", "10169", "10235", "10239"]);
@@ -68,7 +76,7 @@
});
it("ignores undefined trace entry and doesn't discard previously computed UI data", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.entries.length).toEqual(TOTAL_OUTPUT_ENTRIES);
presenter.notifyCurrentTraceEntries(new Map<TraceType, any>());
@@ -76,18 +84,18 @@
});
it("processes trace entry and updates current entry and scroll position", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.currentEntryIndex).toEqual(0);
expect(outputUiData!.scrollToIndex).toEqual(0);
- (<TransactionsTraceEntry>inputTraceEntries.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ (<TransactionsTraceEntry>inputTraceEntriesElapsed.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.currentEntryIndex).toEqual(13);
expect(outputUiData!.scrollToIndex).toEqual(13);
});
it("filters entries according to PID filter", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
presenter.onPidFilterChanged([]);
expect(new Set(outputUiData!.entries.map(entry => entry.pid)))
@@ -103,7 +111,7 @@
});
it("filters entries according to UID filter", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
presenter.onUidFilterChanged([]);
expect(new Set(outputUiData!.entries.map(entry => entry.uid)))
@@ -119,7 +127,7 @@
});
it("filters entries according to type filter", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
presenter.onTypeFilterChanged([]);
expect(new Set(outputUiData!.entries.map(entry => entry.type)))
@@ -141,7 +149,7 @@
});
it("filters entries according to ID filter", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
presenter.onIdFilterChanged([]);
expect(new Set(outputUiData!.entries.map(entry => entry.id)).size)
@@ -157,7 +165,7 @@
});
it ("updates selected entry and properties tree when entry is clicked", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.currentEntryIndex).toEqual(0);
expect(outputUiData!.selectedEntryIndex).toBeUndefined();
expect(outputUiData!.scrollToIndex).toEqual(0);
@@ -181,17 +189,17 @@
});
it("computes current entry index", () => {
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.currentEntryIndex).toEqual(0);
- (<TransactionsTraceEntry>inputTraceEntries.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ (<TransactionsTraceEntry>inputTraceEntriesElapsed.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
expect(outputUiData!.currentEntryIndex).toEqual(13);
});
it("updates current entry index when filters change", () => {
- (<TransactionsTraceEntry>inputTraceEntries.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
- presenter.notifyCurrentTraceEntries(inputTraceEntries);
+ (<TransactionsTraceEntry>inputTraceEntriesElapsed.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
presenter.onPidFilterChanged([]);
expect(outputUiData!.currentEntryIndex).toEqual(13);
@@ -205,4 +213,18 @@
presenter.onPidFilterChanged(["0", "515", "N/A"]);
expect(outputUiData!.currentEntryIndex).toEqual(13);
});
+
+ it("formats real time", () => {
+ (<TransactionsTraceEntry>inputTraceEntriesReal.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesReal);
+
+ expect(outputUiData!.entries[0].time).toEqual("06h19m01s051ms480997ns, 3 Aug 2022 UTC");
+ });
+
+ it("formats elapsed time", () => {
+ (<TransactionsTraceEntry>inputTraceEntriesElapsed.get(TraceType.TRANSACTIONS)[0]).currentEntryIndex = 10;
+ presenter.notifyCurrentTraceEntries(inputTraceEntriesElapsed);
+
+ expect(outputUiData!.entries[0].time).toEqual("2s450ms");
+ });
});
diff --git a/tools/winscope-ng/src/viewers/viewer_transactions/presenter.ts b/tools/winscope-ng/src/viewers/viewer_transactions/presenter.ts
index ec1e53e..cb95a69 100644
--- a/tools/winscope-ng/src/viewers/viewer_transactions/presenter.ts
+++ b/tools/winscope-ng/src/viewers/viewer_transactions/presenter.ts
@@ -20,6 +20,7 @@
import {PropertiesTreeGenerator} from "viewers/common/properties_tree_generator";
import {PropertiesTreeNode} from "viewers/common/ui_tree_utils";
import {TimeUtils} from "common/utils/time_utils";
+import { TimestampType } from "common/trace/timestamp";
class Presenter {
constructor(notifyUiDataCallback: (data: UiData) => void) {
@@ -95,7 +96,7 @@
return;
}
- const entries = this.makeUiDataEntries(this.entry!.entriesProto);
+ const entries = this.makeUiDataEntries(this.entry!);
const allPids = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) => entry.pid);
const allUids = this.getUniqueUiDataEntryValues(entries, (entry: UiDataEntry) => entry.uid);
@@ -166,7 +167,10 @@
return undefined;
}
- private makeUiDataEntries(entriesProto: any[]): UiDataEntry[] {
+ private makeUiDataEntries(entry: TransactionsTraceEntry): UiDataEntry[] {
+ const entriesProto: any[] = entry.entriesProto;
+ const timestampType = entry.timestampType;
+ const realToElapsedTimeOffsetNs = entry.realToElapsedTimeOffsetNs;
const treeGenerator = new PropertiesTreeGenerator();
const entries: UiDataEntry[] = [];
@@ -176,7 +180,7 @@
for (const layerStateProto of transactionStateProto.layerChanges) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
transactionStateProto.pid.toString(),
transactionStateProto.uid.toString(),
@@ -189,7 +193,7 @@
for (const displayStateProto of transactionStateProto.displayChanges) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
transactionStateProto.pid.toString(),
transactionStateProto.uid.toString(),
@@ -203,7 +207,7 @@
for (const layerCreationArgsProto of entryProto.addedLayers) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
Presenter.VALUE_NA,
Presenter.VALUE_NA,
@@ -216,7 +220,7 @@
for (const removedLayerId of entryProto.removedLayers) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
Presenter.VALUE_NA,
Presenter.VALUE_NA,
@@ -229,7 +233,7 @@
for (const displayStateProto of entryProto.addedDisplays) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
Presenter.VALUE_NA,
Presenter.VALUE_NA,
@@ -242,7 +246,7 @@
for (const removedDisplayId of entryProto.removedDisplays) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
Presenter.VALUE_NA,
Presenter.VALUE_NA,
@@ -255,7 +259,7 @@
for (const removedLayerHandleId of entryProto.removedLayerHandles) {
entries.push(new UiDataEntry(
originalIndex,
- TimeUtils.nanosecondsToHuman(Number(entryProto.elapsedRealtimeNanos)),
+ this.formatTime(entryProto, timestampType, realToElapsedTimeOffsetNs),
Number(entryProto.vsyncId),
Presenter.VALUE_NA,
Presenter.VALUE_NA,
@@ -269,6 +273,14 @@
return entries;
}
+ private formatTime(entryProto: any, timestampType: TimestampType, realToElapsedTimeOffsetNs: bigint|undefined): string {
+ if (timestampType === TimestampType.REAL && realToElapsedTimeOffsetNs !== undefined) {
+ return TimeUtils.nanosecondsToHumanReal(BigInt(entryProto.elapsedRealtimeNanos) + realToElapsedTimeOffsetNs);
+ } else {
+ return TimeUtils.nanosecondsToHumanElapsed(Number(entryProto.elapsedRealtimeNanos));
+ }
+ }
+
private getUniqueUiDataEntryValues(entries: UiDataEntry[], getValue: (entry: UiDataEntry) => string): string[] {
const uniqueValues = new Set<string>();
entries.forEach((entry: UiDataEntry) => {