Squashed timestamp refactor (for reference).
Bug: 332662639
Test: npm run test:unit:ci
Change-Id: Ib39341814dc34ebd503b1a427e40fd633ded96b9
diff --git a/tools/winscope/src/app/components/app_component_test.ts b/tools/winscope/src/app/components/app_component_test.ts
index 766606c..16bc612 100644
--- a/tools/winscope/src/app/components/app_component_test.ts
+++ b/tools/winscope/src/app/components/app_component_test.ts
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import {ClipboardModule} from '@angular/cdk/clipboard';
import {CommonModule} from '@angular/common';
import {ChangeDetectionStrategy} from '@angular/core';
import {
@@ -37,11 +38,9 @@
import {MatSnackBarModule} from '@angular/material/snack-bar';
import {MatToolbarModule} from '@angular/material/toolbar';
import {MatTooltipModule} from '@angular/material/tooltip';
+import {Title} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
-
-import {ClipboardModule} from '@angular/cdk/clipboard';
-import {Title} from '@angular/platform-browser';
import {FileUtils} from 'common/file_utils';
import {
AppRefreshDumpsRequest,
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component.ts
index aaba8c0..2ef5c55 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component.ts
@@ -26,7 +26,7 @@
import {Point} from 'common/geometry_types';
import {Rect} from 'common/rect';
import {TimeRange, Timestamp} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
import {Trace, TraceEntry} from 'trace/trace';
import {TracePosition} from 'trace/trace_position';
import {AbstractTimelineRowComponent} from './abstract_timeline_row_component';
@@ -52,6 +52,7 @@
@Input() trace: Trace<{}> | undefined;
@Input() selectedEntry: TraceEntry<{}> | undefined;
@Input() selectionRange: TimeRange | undefined;
+ @Input() timestampConverter: ComponentTimestampConverter | undefined;
@Output() readonly onTracePositionUpdate = new EventEmitter<TracePosition>();
@@ -172,11 +173,7 @@
(BigInt(Math.floor(x)) * (end - start)) /
BigInt(this.getAvailableWidth()) +
start;
- return NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- assertDefined(this.selectionRange).from.getType(),
- ts,
- 0n,
- );
+ return assertDefined(this.timestampConverter).makeTimestampFromNs(ts);
}
private drawEntry(entry: Timestamp) {
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts
index 792c15a..2c9e56d 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/default_timeline_row_component_test.ts
@@ -27,7 +27,7 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
import {Rect} from 'common/rect';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {waitToBeCalled} from 'test/utils';
import {TraceType} from 'trace/trace_type';
@@ -252,16 +252,17 @@
.setType(TraceType.TRANSITION)
.setEntries([{}, {}, {}, {}])
.setTimestamps([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(15n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(70n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(12n),
+ TimestampConverterUtils.makeRealTimestamp(15n),
+ TimestampConverterUtils.makeRealTimestamp(70n),
])
.build();
component.selectionRange = {
- from: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(low),
- to: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(high),
+ from: TimestampConverterUtils.makeRealTimestamp(low),
+ to: TimestampConverterUtils.makeRealTimestamp(high),
};
+ component.timestampConverter = TimestampConverterUtils.TIMESTAMP_CONVERTER;
}
async function drawCorrectEntryOnClick(
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component.ts
index 1f934b7..6644c2a 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component.ts
@@ -55,6 +55,7 @@
[traceEntries]="timelineData.getTransitions()"
[selectedEntry]="timelineData.findCurrentEntryFor(trace.type)"
[selectionRange]="timelineData.getSelectionTimeRange()"
+ [timestampConverter]="timelineData.getTimestampConverter()"
(onTracePositionUpdate)="onTracePositionUpdate.emit($event)"
(onScrollEvent)="updateScroll($event)"
class="single-timeline">
@@ -65,6 +66,7 @@
[trace]="trace"
[selectedEntry]="timelineData.findCurrentEntryFor(trace.type)"
[selectionRange]="timelineData.getSelectionTimeRange()"
+ [timestampConverter]="timelineData.getTimestampConverter()"
(onTracePositionUpdate)="onTracePositionUpdate.emit($event)"
(onScrollEvent)="updateScroll($event)"
class="single-timeline">
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
index 0cabdaa..c71ba8e 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/expanded_timeline_component_test.ts
@@ -27,8 +27,8 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {TimelineData} from 'app/timeline_data';
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -41,12 +41,12 @@
let component: ExpandedTimelineComponent;
let htmlElement: HTMLElement;
let timelineData: TimelineData;
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const time11 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n);
- const time12 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n);
- const time30 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(30n);
- const time60 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(60n);
- const time110 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const time11 = TimestampConverterUtils.makeRealTimestamp(11n);
+ const time12 = TimestampConverterUtils.makeRealTimestamp(12n);
+ const time30 = TimestampConverterUtils.makeRealTimestamp(30n);
+ const time60 = TimestampConverterUtils.makeRealTimestamp(60n);
+ const time110 = TimestampConverterUtils.makeRealTimestamp(110n);
beforeEach(async () => {
await TestBed.configureTestingModule({
@@ -120,7 +120,11 @@
.setTimestamps(TraceType.TRANSITION, [time10, time60])
.setTimestamps(TraceType.PROTO_LOG, [])
.build();
- await timelineData.initialize(traces, undefined);
+ await timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
component.timelineData = timelineData;
});
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
index f2cf12b..6e3e971 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component.ts
@@ -27,6 +27,7 @@
import {Point} from 'common/geometry_types';
import {Rect} from 'common/rect';
import {TimeRange, Timestamp} from 'common/time';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
import {Trace, TraceEntry} from 'trace/trace';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -58,6 +59,7 @@
@Input() traceEntries: PropertyTreeNode[] | undefined;
@Input() selectedEntry: TraceEntry<PropertyTreeNode> | undefined;
@Input() selectionRange: TimeRange | undefined;
+ @Input() timestampConverter: ComponentTimestampConverter | undefined;
@Output() readonly onTracePositionUpdate = new EventEmitter<TracePosition>();
@@ -104,8 +106,8 @@
}
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- entry.getTimestamp().getType(),
assertDefined(this.selectionRange),
+ assertDefined(this.timestampConverter),
);
if (!timeRange) {
return;
@@ -133,8 +135,8 @@
}
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- entry.getTimestamp().getType(),
assertDefined(this.selectionRange),
+ assertDefined(this.timestampConverter),
);
if (!timeRange) {
@@ -170,8 +172,8 @@
}
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- this.hoveringEntry.getTimestamp().getType(),
assertDefined(this.selectionRange),
+ assertDefined(this.timestampConverter),
);
if (!timeRange) {
@@ -252,8 +254,8 @@
}
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- this.selectedEntry.getTimestamp().getType(),
assertDefined(this.selectionRange),
+ assertDefined(this.timestampConverter),
);
if (!timeRange) {
return;
@@ -286,8 +288,8 @@
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- entry.getTimestamp().getType(),
assertDefined(this.selectionRange),
+ assertDefined(this.timestampConverter),
);
if (!timeRange) {
diff --git a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
index 3b46b73..1badb3a 100644
--- a/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/expanded-timeline/transition_timeline_component_test.ts
@@ -26,8 +26,8 @@
import {MatTooltipModule} from '@angular/material/tooltip';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {Rect} from 'common/rect';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {waitToBeCalled} from 'test/utils';
import {TraceType} from 'trace/trace_type';
@@ -37,15 +37,15 @@
describe('TransitionTimelineComponent', () => {
let fixture: ComponentFixture<TransitionTimelineComponent>;
let component: TransitionTimelineComponent;
- const time0 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const time20 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(20n);
- const time30 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(30n);
- const time35 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(35n);
- const time60 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(60n);
- const time85 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(85n);
- const time110 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n);
- const time160 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(160n);
+ const time0 = TimestampConverterUtils.makeRealTimestamp(0n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const time20 = TimestampConverterUtils.makeRealTimestamp(20n);
+ const time30 = TimestampConverterUtils.makeRealTimestamp(30n);
+ const time35 = TimestampConverterUtils.makeRealTimestamp(35n);
+ const time60 = TimestampConverterUtils.makeRealTimestamp(60n);
+ const time85 = TimestampConverterUtils.makeRealTimestamp(85n);
+ const time110 = TimestampConverterUtils.makeRealTimestamp(110n);
+ const time160 = TimestampConverterUtils.makeRealTimestamp(160n);
beforeEach(async () => {
await TestBed.configureTestingModule({
@@ -69,6 +69,7 @@
.compileComponents();
fixture = TestBed.createComponent(TransitionTimelineComponent);
component = fixture.componentInstance;
+ component.timestampConverter = TimestampConverterUtils.TIMESTAMP_CONVERTER;
});
it('can be created', () => {
@@ -115,14 +116,14 @@
.setType(TraceType.TRANSITION)
.setEntries(transitions)
.setTimestamps([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(60n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(60n),
])
.build();
component.traceEntries = transitions;
component.selectionRange = {
- from: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- to: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n),
+ from: TimestampConverterUtils.makeRealTimestamp(10n),
+ to: TimestampConverterUtils.makeRealTimestamp(110n),
};
const drawRectSpy = spyOn(component.canvasDrawer, 'drawRect');
@@ -614,12 +615,12 @@
component.trace = new TraceBuilder<PropertyTreeNode>()
.setType(TraceType.TRANSITION)
.setEntries(transitions)
- .setTimestamps([NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n)])
+ .setTimestamps([TimestampConverterUtils.makeRealTimestamp(10n)])
.build();
component.traceEntries = transitions;
component.selectionRange = {
- from: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- to: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n),
+ from: TimestampConverterUtils.makeRealTimestamp(10n),
+ to: TimestampConverterUtils.makeRealTimestamp(110n),
};
fixture.detectChanges();
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts b/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
index de2c408..a472f35 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/drawer/mini_timeline_drawer_input.ts
@@ -37,7 +37,11 @@
) {}
transform(mapToRange: Segment): MiniCanvasDrawerData {
- const transformer = new Transformer(this.zoomRange, mapToRange);
+ const transformer = new Transformer(
+ this.zoomRange,
+ mapToRange,
+ assertDefined(this.timelineData.getTimestampConverter()),
+ );
return new MiniCanvasDrawerData(
transformer.transform(this.selectedPosition),
@@ -117,8 +121,8 @@
const timeRange = TimelineUtils.getTimeRangeForTransition(
transition,
- entry.getTimestamp().getType(),
this.selection,
+ assertDefined(this.timelineData.getTimestampConverter()),
);
if (!timeRange) {
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component.ts b/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component.ts
index e7b262d..8a25a9a 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component.ts
@@ -59,6 +59,7 @@
[fullRange]="timelineData.getFullTimeRange()"
[zoomRange]="timelineData.getZoomRange()"
[currentPosition]="timelineData.getCurrentPosition()"
+ [timestampConverter]="timelineData.getTimestampConverter()"
(onZoomChanged)="onZoomChanged($event)"></slider>
</div>
</div>
@@ -214,14 +215,16 @@
const timelineData = assertDefined(this.timelineData);
const fullRange = timelineData.getFullTimeRange();
const currentZoomRange = timelineData.getZoomRange();
- const currentZoomWidth = currentZoomRange.to.minus(currentZoomRange.from);
+ const currentZoomWidth = currentZoomRange.to.minus(
+ currentZoomRange.from.getValueNs(),
+ );
const zoomToWidth = currentZoomWidth
.times(zoomRatio.nominator)
.div(zoomRatio.denominator);
const cursorPosition = timelineData.getCurrentPosition()?.timestamp;
const currentMiddle = currentZoomRange.from
- .plus(currentZoomRange.to)
+ .add(currentZoomRange.to.getValueNs())
.div(2n);
let newFrom: Timestamp;
@@ -246,9 +249,9 @@
rightAdjustment = currentZoomWidth.times(0n);
}
- newFrom = currentZoomRange.from.plus(leftAdjustment);
- newTo = currentZoomRange.to.minus(rightAdjustment);
- const newMiddle = newFrom.plus(newTo).div(2n);
+ newFrom = currentZoomRange.from.add(leftAdjustment.getValueNs());
+ newTo = currentZoomRange.to.minus(rightAdjustment.getValueNs());
+ const newMiddle = newFrom.add(newTo.getValueNs()).div(2n);
if (
(zoomTowards.getValueNs() <= currentMiddle.getValueNs() &&
@@ -257,18 +260,18 @@
newMiddle.getValueNs() > zoomTowards.getValueNs())
) {
// Moved past middle, so ensure cursor is in the middle
- newFrom = zoomTowards.minus(zoomToWidth.div(2n));
- newTo = zoomTowards.plus(zoomToWidth.div(2n));
+ newFrom = zoomTowards.minus(zoomToWidth.div(2n).getValueNs());
+ newTo = zoomTowards.add(zoomToWidth.div(2n).getValueNs());
}
} else {
- newFrom = zoomOn.minus(zoomToWidth.div(2n));
- newTo = zoomOn.plus(zoomToWidth.div(2n));
+ newFrom = zoomOn.minus(zoomToWidth.div(2n).getValueNs());
+ newTo = zoomOn.add(zoomToWidth.div(2n).getValueNs());
}
if (newFrom.getValueNs() < fullRange.from.getValueNs()) {
newTo = TimestampUtils.min(
fullRange.to,
- newTo.plus(fullRange.from.minus(newFrom)),
+ newTo.add(fullRange.from.minus(newFrom.getValueNs()).getValueNs()),
);
newFrom = fullRange.from;
}
@@ -276,7 +279,7 @@
if (newTo.getValueNs() > fullRange.to.getValueNs()) {
newFrom = TimestampUtils.max(
fullRange.from,
- newFrom.minus(newTo.minus(fullRange.to)),
+ newFrom.minus(newTo.minus(fullRange.to.getValueNs()).getValueNs()),
);
newTo = fullRange.to;
}
@@ -373,13 +376,15 @@
}
private updateZoomByScrollEvent(event: WheelEvent) {
+ const timelineData = assertDefined(this.timelineData);
const canvas = event.target as HTMLCanvasElement;
const xPosInCanvas = event.x - canvas.offsetLeft;
- const zoomRange = assertDefined(this.timelineData).getZoomRange();
+ const zoomRange = timelineData.getZoomRange();
const zoomTo = new Transformer(
zoomRange,
assertDefined(this.drawer).getUsableRange(),
+ assertDefined(timelineData.getTimestampConverter()),
).untransform(xPosInCanvas);
if (event.deltaY < 0) {
@@ -396,20 +401,28 @@
const zoomRange = timelineData.getZoomRange();
const usableRange = assertDefined(this.drawer).getUsableRange();
- const transformer = new Transformer(zoomRange, usableRange);
+ const transformer = new Transformer(
+ zoomRange,
+ usableRange,
+ assertDefined(timelineData.getTimestampConverter()),
+ );
const shiftAmount = transformer
.untransform(usableRange.from + scrollAmount)
- .minus(zoomRange.from);
- let newFrom = zoomRange.from.plus(shiftAmount);
- let newTo = zoomRange.to.plus(shiftAmount);
+ .minus(zoomRange.from.getValueNs());
+ let newFrom = zoomRange.from.add(shiftAmount.getValueNs());
+ let newTo = zoomRange.to.add(shiftAmount.getValueNs());
if (newFrom.getValueNs() < fullRange.from.getValueNs()) {
- newTo = newTo.plus(fullRange.from.minus(newFrom));
+ newTo = newTo.add(
+ fullRange.from.minus(newFrom.getValueNs()).getValueNs(),
+ );
newFrom = fullRange.from;
}
if (newTo.getValueNs() > fullRange.to.getValueNs()) {
- newFrom = newFrom.minus(newTo.minus(fullRange.to));
+ newFrom = newFrom.minus(
+ newTo.minus(fullRange.to.getValueNs()).getValueNs(),
+ );
newTo = fullRange.to;
}
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts b/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts
index 41e57b5..9f2f591 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/mini_timeline_component_test.ts
@@ -27,7 +27,7 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {TimelineData} from 'app/timeline_data';
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {dragElement} from 'test/utils';
import {TracePosition} from 'trace/trace_position';
@@ -41,16 +41,16 @@
let htmlElement: HTMLElement;
let timelineData: TimelineData;
- const timestamp10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const timestamp15 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(15n);
- const timestamp16 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(16n);
- const timestamp20 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(20n);
- const timestamp700 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(700n);
- const timestamp810 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(810n);
- const timestamp1000 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1000n);
+ const timestamp10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const timestamp15 = TimestampConverterUtils.makeRealTimestamp(15n);
+ const timestamp16 = TimestampConverterUtils.makeRealTimestamp(16n);
+ const timestamp20 = TimestampConverterUtils.makeRealTimestamp(20n);
+ const timestamp700 = TimestampConverterUtils.makeRealTimestamp(700n);
+ const timestamp810 = TimestampConverterUtils.makeRealTimestamp(810n);
+ const timestamp1000 = TimestampConverterUtils.makeRealTimestamp(1000n);
const position800 = TracePosition.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(800n),
+ TimestampConverterUtils.makeRealTimestamp(800n),
);
beforeEach(async () => {
@@ -83,7 +83,11 @@
.setTimestamps(TraceType.TRANSACTIONS, [timestamp10, timestamp20])
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp20])
.build();
- await timelineData.initialize(traces, undefined);
+ await timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
component.timelineData = timelineData;
expect(timelineData.getCurrentPosition()).toBeTruthy();
component.currentTracePosition = timelineData.getCurrentPosition()!;
@@ -256,7 +260,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
let initialZoom = {
from: timestamp10,
@@ -285,14 +293,21 @@
expect(finalZoom.to.getValueNs()).toBeLessThanOrEqual(
Number(initialZoom.to.getValueNs()),
);
- expect(finalZoom.to.minus(finalZoom.from).getValueNs()).toBeLessThan(
- Number(initialZoom.to.minus(initialZoom.from).getValueNs()),
+ expect(
+ finalZoom.to.minus(finalZoom.from.getValueNs()).getValueNs(),
+ ).toBeLessThan(
+ Number(
+ initialZoom.to.minus(initialZoom.from.getValueNs()).getValueNs(),
+ ),
);
// center to get closer to cursor or stay on cursor
- const curCenter = finalZoom.from.plus(finalZoom.to).div(2n).getValueNs();
+ const curCenter = finalZoom.from
+ .add(finalZoom.to.getValueNs())
+ .div(2n)
+ .getValueNs();
const prevCenter = initialZoom.from
- .plus(initialZoom.to)
+ .add(initialZoom.to.getValueNs())
.div(2n)
.getValueNs();
@@ -316,7 +331,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
let initialZoom = {
from: timestamp700,
@@ -345,14 +364,21 @@
expect(finalZoom.to.getValueNs()).toBeGreaterThanOrEqual(
Number(initialZoom.to.getValueNs()),
);
- expect(finalZoom.to.minus(finalZoom.from).getValueNs()).toBeGreaterThan(
- Number(initialZoom.to.minus(initialZoom.from).getValueNs()),
+ expect(
+ finalZoom.to.minus(finalZoom.from.getValueNs()).getValueNs(),
+ ).toBeGreaterThan(
+ Number(
+ initialZoom.to.minus(initialZoom.from.getValueNs()).getValueNs(),
+ ),
);
// center to get closer to cursor or stay on cursor unless we reach the edge
- const curCenter = finalZoom.from.plus(finalZoom.to).div(2n).getValueNs();
+ const curCenter = finalZoom.from
+ .add(finalZoom.to.getValueNs())
+ .div(2n)
+ .getValueNs();
const prevCenter = initialZoom.from
- .plus(initialZoom.to)
+ .add(initialZoom.to.getValueNs())
.div(2n)
.getValueNs();
@@ -380,7 +406,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
const initialZoom = {
from: timestamp10,
@@ -410,7 +440,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
let initialZoom = {
from: timestamp10,
@@ -431,8 +465,12 @@
fixture.detectChanges();
const finalZoom = timelineData.getZoomRange();
expect(finalZoom).not.toBe(initialZoom);
- expect(finalZoom.to.minus(finalZoom.from).getValueNs()).toBeLessThan(
- Number(initialZoom.to.minus(initialZoom.from).getValueNs()),
+ expect(
+ finalZoom.to.minus(finalZoom.from.getValueNs()).getValueNs(),
+ ).toBeLessThan(
+ Number(
+ initialZoom.to.minus(initialZoom.from.getValueNs()).getValueNs(),
+ ),
);
initialZoom = finalZoom;
@@ -446,7 +484,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
let initialZoom = {
from: timestamp700,
@@ -467,8 +509,12 @@
fixture.detectChanges();
const finalZoom = timelineData.getZoomRange();
expect(finalZoom).not.toBe(initialZoom);
- expect(finalZoom.to.minus(finalZoom.from).getValueNs()).toBeGreaterThan(
- Number(initialZoom.to.minus(initialZoom.from).getValueNs()),
+ expect(
+ finalZoom.to.minus(finalZoom.from.getValueNs()).getValueNs(),
+ ).toBeGreaterThan(
+ Number(
+ initialZoom.to.minus(initialZoom.from.getValueNs()).getValueNs(),
+ ),
);
initialZoom = finalZoom;
@@ -482,7 +528,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
const initialZoom = {
from: timestamp10,
@@ -511,7 +561,11 @@
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp1000])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
const initialZoom = {
from: timestamp10,
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/slider_component.ts b/tools/winscope/src/app/components/timeline/mini-timeline/slider_component.ts
index ac085fe..16476b7 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/slider_component.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/slider_component.ts
@@ -30,7 +30,7 @@
import {assertDefined} from 'common/assert_utils';
import {Point} from 'common/geometry_types';
import {TimeRange, Timestamp} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
import {TracePosition} from 'trace/trace_position';
import {Transformer} from './transformer';
@@ -123,6 +123,7 @@
@Input() fullRange: TimeRange | undefined;
@Input() zoomRange: TimeRange | undefined;
@Input() currentPosition: TracePosition | undefined;
+ @Input() timestampConverter: ComponentTimestampConverter | undefined;
@Output() readonly onZoomChanged = new EventEmitter<TimeRange>();
@@ -151,8 +152,8 @@
syncDragPositionTo(zoomRange: TimeRange) {
this.sliderWidth = this.computeSliderWidth();
- const middleOfZoomRange = zoomRange.from.plus(
- zoomRange.to.minus(zoomRange.from).div(2n),
+ const middleOfZoomRange = zoomRange.from.add(
+ zoomRange.to.minus(zoomRange.from.getValueNs()).div(2n).getValueNs(),
);
this.dragPosition = {
@@ -172,7 +173,11 @@
const width = this.viewInitialized
? this.sliderBox.nativeElement.offsetWidth
: 0;
- return new Transformer(assertDefined(this.fullRange), {from: 0, to: width});
+ return new Transformer(
+ assertDefined(this.fullRange),
+ {from: 0, to: width},
+ assertDefined(this.timestampConverter),
+ );
}
ngAfterViewInit(): void {
@@ -230,14 +235,14 @@
// Calculation to adjust for min width slider
const from = this.getTransformer()
.untransform(newX + this.sliderWidth / 2)
- .minus(zoomRange.to.minus(zoomRange.from).div(2n));
+ .minus(
+ zoomRange.to.minus(zoomRange.from.getValueNs()).div(2n).getValueNs(),
+ );
- const to = NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- assertDefined(this.zoomRange).to.getType(),
+ const to = assertDefined(this.timestampConverter).makeTimestampFromNs(
from.getValueNs() +
(assertDefined(this.zoomRange).to.getValueNs() -
assertDefined(this.zoomRange).from.getValueNs()),
- 0n,
);
this.onZoomChanged.emit({from, to});
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts b/tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts
index d002dce..fe82a54 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/slider_component_test.ts
@@ -27,7 +27,7 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
import {TimeRange} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {dragElement} from 'test/utils';
import {TracePosition} from 'trace/trace_position';
import {MIN_SLIDER_WIDTH, SliderComponent} from './slider_component';
@@ -36,12 +36,12 @@
let fixture: ComponentFixture<SliderComponent>;
let component: SliderComponent;
let htmlElement: HTMLElement;
- const time100 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n);
- const time125 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(125n);
- const time126 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(126n);
- const time150 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(150n);
- const time175 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(175n);
- const time200 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(200n);
+ const time100 = TimestampConverterUtils.makeRealTimestamp(100n);
+ const time125 = TimestampConverterUtils.makeRealTimestamp(125n);
+ const time126 = TimestampConverterUtils.makeRealTimestamp(126n);
+ const time150 = TimestampConverterUtils.makeRealTimestamp(150n);
+ const time175 = TimestampConverterUtils.makeRealTimestamp(175n);
+ const time200 = TimestampConverterUtils.makeRealTimestamp(200n);
beforeEach(async () => {
await TestBed.configureTestingModule({
@@ -76,6 +76,7 @@
to: time175,
};
component.currentPosition = TracePosition.fromTimestamp(time150);
+ component.timestampConverter = TimestampConverterUtils.TIMESTAMP_CONVERTER;
fixture.detectChanges();
});
@@ -201,8 +202,8 @@
const finalZoom = assertDefined<TimeRange>(lastZoomUpdate);
expect(finalZoom.from).not.toBe(initialZoom.from);
expect(finalZoom.to).not.toBe(initialZoom.to);
- expect(finalZoom.to.minus(finalZoom.from).getValueNs()).toBe(
- initialZoom.to.minus(initialZoom.from).getValueNs(),
+ expect(finalZoom.to.minus(finalZoom.from.getValueNs()).getValueNs()).toBe(
+ initialZoom.to.minus(initialZoom.from.getValueNs()).getValueNs(),
);
}));
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/transformer.ts b/tools/winscope/src/app/components/timeline/mini-timeline/transformer.ts
index 533423a..b6e3895 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/transformer.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/transformer.ts
@@ -15,21 +15,21 @@
*/
import {Segment} from 'app/components/timeline/segment';
-import {TimeRange, Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimeRange, Timestamp} from 'common/time';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
export class Transformer {
- private timestampType: TimestampType;
-
private fromWidth: bigint;
private targetWidth: number;
private fromOffset: bigint;
private toOffset: number;
- constructor(private fromRange: TimeRange, private toRange: Segment) {
- this.timestampType = fromRange.from.getType();
-
+ constructor(
+ private fromRange: TimeRange,
+ private toRange: Segment,
+ private timestampConverter: ComponentTimestampConverter,
+ ) {
this.fromWidth =
this.fromRange.to.getValueNs() - this.fromRange.from.getValueNs();
// Needs to be a whole number to be compatible with bigints
@@ -53,10 +53,6 @@
const valueNs =
this.fromOffset +
(BigInt(x - this.toOffset) * this.fromWidth) / BigInt(this.targetWidth);
- return NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- this.timestampType,
- valueNs,
- 0n,
- );
+ return this.timestampConverter.makeTimestampFromNs(valueNs);
}
}
diff --git a/tools/winscope/src/app/components/timeline/mini-timeline/transformer_test.ts b/tools/winscope/src/app/components/timeline/mini-timeline/transformer_test.ts
index 0832c7d..f39e25b 100644
--- a/tools/winscope/src/app/components/timeline/mini-timeline/transformer_test.ts
+++ b/tools/winscope/src/app/components/timeline/mini-timeline/transformer_test.ts
@@ -14,20 +14,24 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {Transformer} from './transformer';
describe('Transformer', () => {
it('can transform', () => {
const fromRange = {
- from: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1689763211000000000n),
- to: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1689763571000000000n),
+ from: TimestampConverterUtils.makeRealTimestamp(1689763211000000000n),
+ to: TimestampConverterUtils.makeRealTimestamp(1689763571000000000n),
};
const toRange = {
from: 100,
to: 1100,
};
- const transformer = new Transformer(fromRange, toRange);
+ const transformer = new Transformer(
+ fromRange,
+ toRange,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
const rangeStart = fromRange.from.getValueNs();
const rangeEnd = fromRange.to.getValueNs();
@@ -38,42 +42,46 @@
expect(
transformer.transform(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(rangeStart + range / 2n),
+ TimestampConverterUtils.makeRealTimestamp(rangeStart + range / 2n),
),
).toBe(toRange.from + (toRange.to - toRange.from) / 2);
expect(
transformer.transform(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(rangeStart + range / 4n),
+ TimestampConverterUtils.makeRealTimestamp(rangeStart + range / 4n),
),
).toBe(toRange.from + (toRange.to - toRange.from) / 4);
expect(
transformer.transform(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(rangeStart + range / 20n),
+ TimestampConverterUtils.makeRealTimestamp(rangeStart + range / 20n),
),
).toBe(toRange.from + (toRange.to - toRange.from) / 20);
expect(
transformer.transform(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(rangeStart - range / 2n),
+ TimestampConverterUtils.makeRealTimestamp(rangeStart - range / 2n),
),
).toBe(toRange.from - (toRange.to - toRange.from) / 2);
expect(
transformer.transform(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(rangeEnd + range / 2n),
+ TimestampConverterUtils.makeRealTimestamp(rangeEnd + range / 2n),
),
).toBe(toRange.to + (toRange.to - toRange.from) / 2);
});
it('can untransform', () => {
const fromRange = {
- from: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1689763211000000000n),
- to: NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1689763571000000000n),
+ from: TimestampConverterUtils.makeRealTimestamp(1689763211000000000n),
+ to: TimestampConverterUtils.makeRealTimestamp(1689763571000000000n),
};
const toRange = {
from: 100,
to: 1100,
};
- const transformer = new Transformer(fromRange, toRange);
+ const transformer = new Transformer(
+ fromRange,
+ toRange,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
const rangeStart = fromRange.from.getValueNs();
const range = fromRange.to.getValueNs() - fromRange.from.getValueNs();
diff --git a/tools/winscope/src/app/components/timeline/timeline_component.ts b/tools/winscope/src/app/components/timeline/timeline_component.ts
index f3d0ffd..9b01820 100644
--- a/tools/winscope/src/app/components/timeline/timeline_component.ts
+++ b/tools/winscope/src/app/components/timeline/timeline_component.ts
@@ -26,7 +26,12 @@
ViewChild,
ViewEncapsulation,
} from '@angular/core';
-import {FormControl, FormGroup, Validators} from '@angular/forms';
+import {
+ FormControl,
+ FormGroup,
+ ValidationErrors,
+ Validators,
+} from '@angular/forms';
import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
import {TimelineData} from 'app/timeline_data';
import {TRACE_INFO} from 'app/trace_info';
@@ -34,8 +39,7 @@
import {FunctionUtils} from 'common/function_utils';
import {PersistentStore} from 'common/persistent_store';
import {StringUtils} from 'common/string_utils';
-import {TimeRange, Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimeRange, Timestamp} from 'common/time';
import {TimestampUtils} from 'common/timestamp_utils';
import {
ExpandedTimelineToggled,
@@ -90,26 +94,14 @@
<div id="time-selector">
<form [formGroup]="timestampForm" class="time-selector-form">
<mat-form-field
- class="time-input elapsed"
+ class="time-input human"
appearance="fill"
- (keydown.enter)="onKeydownEnterElapsedTimeInputField($event)"
- (change)="onHumanElapsedTimeInputChange($event)"
- *ngIf="!usingRealtime()">
+ (keydown.enter)="onKeydownEnterTimeInputField($event)"
+ (change)="onHumanTimeInputChange($event)">
<input
matInput
- name="humanElapsedTimeInput"
- [formControl]="selectedElapsedTimeFormControl" />
- </mat-form-field>
- <mat-form-field
- class="time-input real"
- appearance="fill"
- (keydown.enter)="onKeydownEnterRealTimeInputField($event)"
- (change)="onHumanRealTimeInputChange($event)"
- *ngIf="usingRealtime()">
- <input
- matInput
- name="humanRealTimeInput"
- [formControl]="selectedRealTimeFormControl" />
+ name="humanTimeInput"
+ [formControl]="selectedTimeFormControl" />
</mat-form-field>
<mat-form-field
class="time-input nano"
@@ -413,19 +405,9 @@
selectedTraces: TraceType[] = [];
sortedAvailableTraces: TraceType[] = [];
selectedTracesFormControl = new FormControl<TraceType[]>([]);
- selectedElapsedTimeFormControl = new FormControl(
+ selectedTimeFormControl = new FormControl(
'undefined',
- Validators.compose([
- Validators.required,
- Validators.pattern(TimestampUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX),
- ]),
- );
- selectedRealTimeFormControl = new FormControl(
- 'undefined',
- Validators.compose([
- Validators.required,
- Validators.pattern(TimestampUtils.HUMAN_REAL_TIMESTAMP_REGEX),
- ]),
+ Validators.compose([Validators.required, this.validateTimeFormat]),
);
selectedNsFormControl = new FormControl(
'undefined',
@@ -435,8 +417,7 @@
]),
);
timestampForm = new FormGroup({
- selectedElapsedTime: this.selectedElapsedTimeFormControl,
- selectedRealTime: this.selectedRealTimeFormControl,
+ selectedTime: this.selectedTimeFormControl,
selectedNs: this.selectedNsFormControl,
});
TRACE_INFO = TRACE_INFO;
@@ -555,12 +536,6 @@
await this.emitEvent(new TracePositionUpdate(position));
}
- usingRealtime(): boolean {
- return (
- assertDefined(this.timelineData).getTimestampType() === TimestampType.REAL
- );
- }
-
updateSeekTimestamp(timestamp: Timestamp | undefined) {
if (timestamp) {
this.seekTracePosition = assertDefined(
@@ -675,25 +650,16 @@
await this.emitEvent(new TracePositionUpdate(position));
}
- async onHumanElapsedTimeInputChange(event: Event) {
- if (event.type !== 'change' || !this.selectedElapsedTimeFormControl.valid) {
- return;
- }
- const target = event.target as HTMLInputElement;
- const timestamp = TimestampUtils.parseHumanElapsed(target.value);
- await this.updatePosition(
- assertDefined(this.timelineData).makePositionFromActiveTrace(timestamp),
- );
- this.updateTimeInputValuesToCurrentTimestamp();
- }
-
- async onHumanRealTimeInputChange(event: Event) {
- if (event.type !== 'change' || !this.selectedRealTimeFormControl.valid) {
+ async onHumanTimeInputChange(event: Event) {
+ if (event.type !== 'change' || !this.selectedTimeFormControl.valid) {
return;
}
const target = event.target as HTMLInputElement;
- const timestamp = TimestampUtils.parseHumanReal(target.value);
+ const timelineData = assertDefined(this.timelineData);
+ const timestamp = assertDefined(
+ timelineData.getTimestampConverter(),
+ ).makeTimestampFromHuman(target.value);
await this.updatePosition(
assertDefined(this.timelineData).makePositionFromActiveTrace(timestamp),
);
@@ -707,25 +673,17 @@
const target = event.target as HTMLInputElement;
const timelineData = assertDefined(this.timelineData);
- const timestamp = NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- assertDefined(timelineData.getTimestampType()),
- StringUtils.parseBigIntStrippingUnit(target.value),
- 0n,
- );
+ const timestamp = assertDefined(
+ timelineData.getTimestampConverter(),
+ ).makeTimestampFromNs(StringUtils.parseBigIntStrippingUnit(target.value));
await this.updatePosition(
timelineData.makePositionFromActiveTrace(timestamp),
);
this.updateTimeInputValuesToCurrentTimestamp();
}
- onKeydownEnterElapsedTimeInputField(event: KeyboardEvent) {
- if (this.selectedElapsedTimeFormControl.valid) {
- (event.target as HTMLInputElement).blur();
- }
- }
-
- onKeydownEnterRealTimeInputField(event: KeyboardEvent) {
- if (this.selectedRealTimeFormControl.valid) {
+ onKeydownEnterTimeInputField(event: KeyboardEvent) {
+ if (this.selectedTimeFormControl.valid) {
(event.target as HTMLInputElement).blur();
}
}
@@ -741,21 +699,15 @@
}
private updateTimeInputValuesToCurrentTimestamp() {
- const currentNs = this.getCurrentTracePosition().timestamp.getValueNs();
- this.selectedElapsedTimeFormControl.setValue(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(currentNs),
- false,
- ),
+ const currentTimestampNs =
+ this.getCurrentTracePosition().timestamp.getValueNs();
+ const timelineData = assertDefined(this.timelineData);
+ this.selectedTimeFormControl.setValue(
+ assertDefined(timelineData.getTimestampConverter())
+ .makeTimestampFromNs(currentTimestampNs)
+ .format(),
);
- this.selectedRealTimeFormControl.setValue(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(currentNs),
- ),
- );
- this.selectedNsFormControl.setValue(
- `${this.getCurrentTracePosition().timestamp.getValueNs()} ns`,
- );
+ this.selectedNsFormControl.setValue(`${currentTimestampNs} ns`);
}
private getSelectedTracesSortedByDisplayOrder(): TraceType[] {
@@ -793,4 +745,12 @@
this.store.add(this.storeKeyDeselectedTraces, JSON.stringify(storedTraces));
}
+
+ private validateTimeFormat(control: FormControl): ValidationErrors | null {
+ const timestampHuman = control.value ?? '';
+ const valid =
+ TimestampUtils.HUMAN_REAL_TIMESTAMP_REGEX.test(timestampHuman) ||
+ TimestampUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX.test(timestampHuman);
+ return !valid ? {invalidInput: control.value} : null;
+ }
}
diff --git a/tools/winscope/src/app/components/timeline/timeline_component_test.ts b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
index 0fe6415..af77597 100644
--- a/tools/winscope/src/app/components/timeline/timeline_component_test.ts
+++ b/tools/winscope/src/app/components/timeline/timeline_component_test.ts
@@ -40,8 +40,8 @@
import {TRACE_INFO} from 'app/trace_info';
import {assertDefined} from 'common/assert_utils';
import {PersistentStore} from 'common/persistent_store';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {ExpandedTimelineToggled, WinscopeEvent} from 'messaging/winscope_event';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -53,12 +53,12 @@
import {TimelineComponent} from './timeline_component';
describe('TimelineComponent', () => {
- const time90 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(90n);
- const time100 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n);
- const time101 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(101n);
- const time105 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(105n);
- const time110 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n);
- const time112 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(112n);
+ const time90 = TimestampConverterUtils.makeRealTimestamp(90n);
+ const time100 = TimestampConverterUtils.makeRealTimestamp(100n);
+ const time101 = TimestampConverterUtils.makeRealTimestamp(101n);
+ const time105 = TimestampConverterUtils.makeRealTimestamp(105n);
+ const time110 = TimestampConverterUtils.makeRealTimestamp(110n);
+ const time112 = TimestampConverterUtils.makeRealTimestamp(112n);
const position90 = TracePosition.fromTimestamp(time90);
const position100 = TracePosition.fromTimestamp(time100);
@@ -114,7 +114,11 @@
const traces = new TracesBuilder()
.setTimestamps(TraceType.SURFACE_FLINGER, [time100, time110])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
fixture.detectChanges();
const timelineComponent = assertDefined(component.timeline);
@@ -157,6 +161,7 @@
assertDefined(assertDefined(component.timelineData)).initialize(
traces,
undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
);
fixture.detectChanges();
@@ -202,7 +207,11 @@
.setTimestamps(TraceType.SURFACE_FLINGER, [])
.setTimestamps(TraceType.WINDOW_MANAGER, [time100])
.build();
- assertDefined(component.timelineData).initialize(traces, undefined);
+ assertDefined(component.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
fixture.detectChanges();
});
@@ -573,7 +582,7 @@
?.timestamp.getValueNs(),
).toEqual(100n);
const timeInputField = assertDefined(
- fixture.debugElement.query(By.css('.time-input.real')),
+ fixture.debugElement.query(By.css('.time-input.human')),
);
testCurrentTimestampOnTimeInput(
@@ -766,7 +775,11 @@
.build();
const timelineData = assertDefined(hostComponent.timelineData);
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
hostComponent.availableTraces = [
TraceType.SURFACE_FLINGER,
TraceType.WINDOW_MANAGER,
@@ -783,7 +796,11 @@
.setTimestamps(TraceType.SCREEN_RECORDING, [time110])
.setTimestamps(TraceType.PROTO_LOG, [time100])
.build();
- assertDefined(hostComponent.timelineData).initialize(traces, undefined);
+ assertDefined(hostComponent.timelineData).initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
hostComponent.availableTraces = [
TraceType.SURFACE_FLINGER,
TraceType.WINDOW_MANAGER,
diff --git a/tools/winscope/src/app/components/timeline/timeline_utils.ts b/tools/winscope/src/app/components/timeline/timeline_utils.ts
index 350b1f4..62d776a 100644
--- a/tools/winscope/src/app/components/timeline/timeline_utils.ts
+++ b/tools/winscope/src/app/components/timeline/timeline_utils.ts
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-import {TimeRange, Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimeRange, Timestamp} from 'common/time';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
export class TimelineUtils {
static getTimeRangeForTransition(
transition: PropertyTreeNode,
- timestampType: TimestampType,
fullTimeRange: TimeRange,
+ converter: ComponentTimestampConverter,
): TimeRange | undefined {
const shellData = transition.getChildByName('shellData');
const wmData = transition.getChildByName('wmData');
@@ -50,50 +50,20 @@
return undefined;
}
- let dispatchTimeNs: bigint | undefined;
- let finishTimeNs: bigint | undefined;
-
const timeRangeMin = fullTimeRange.from.getValueNs();
const timeRangeMax = fullTimeRange.to.getValueNs();
- if (timestampType === TimestampType.ELAPSED) {
- const startOffset =
- shellData
- ?.getChildByName('realToElapsedTimeOffsetTimestamp')
- ?.getValue()
- .getValueNs() ?? 0n;
- const finishOffset = aborted
- ? startOffset
- : shellData
- ?.getChildByName('realToElapsedTimeOffsetTimestamp')
- ?.getValue()
- .getValueNs() ?? 0n;
+ const dispatchTimeNs = dispatchTimestamp
+ ? dispatchTimestamp.getValueNs()
+ : timeRangeMin;
+ const finishTimeNs = finishOrAbortTimestamp
+ ? finishOrAbortTimestamp.getValueNs()
+ : timeRangeMax;
- dispatchTimeNs = dispatchTimestamp
- ? dispatchTimestamp.getValueNs() - startOffset
- : timeRangeMin;
- finishTimeNs = finishOrAbortTimestamp
- ? finishOrAbortTimestamp.getValueNs() - finishOffset
- : timeRangeMax;
- } else {
- dispatchTimeNs = dispatchTimestamp
- ? dispatchTimestamp.getValueNs()
- : timeRangeMin;
- finishTimeNs = finishOrAbortTimestamp
- ? finishOrAbortTimestamp.getValueNs()
- : timeRangeMax;
- }
-
- const startTime = NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- timestampType,
+ const startTime = converter.makeTimestampFromNs(
dispatchTimeNs > timeRangeMin ? dispatchTimeNs : timeRangeMin,
- 0n,
);
- const finishTime = NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(
- timestampType,
- finishTimeNs,
- 0n,
- );
+ const finishTime = converter.makeTimestampFromNs(finishTimeNs);
return {
from: startTime,
diff --git a/tools/winscope/src/app/loaded_parsers.ts b/tools/winscope/src/app/loaded_parsers.ts
index 1ac0efe..e5ecd72 100644
--- a/tools/winscope/src/app/loaded_parsers.ts
+++ b/tools/winscope/src/app/loaded_parsers.ts
@@ -15,7 +15,8 @@
*/
import {FileUtils} from 'common/file_utils';
-import {INVALID_TIME_NS, TimeRange, TimestampType} from 'common/time';
+import {INVALID_TIME_NS, TimeRange} from 'common/time';
+import {TIME_UNIT_TO_NANO} from 'common/time_units';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
import {TraceHasOldData, TraceOverridden} from 'messaging/user_warnings';
import {FileAndParser} from 'parsers/file_and_parser';
@@ -26,8 +27,16 @@
import {TRACE_INFO} from './trace_info';
export class LoadedParsers {
- static readonly MAX_ALLOWED_TIME_GAP_BETWEEN_TRACES_NS =
- 5n * 60n * 1000000000n; // 5m
+ static readonly MAX_ALLOWED_TIME_GAP_BETWEEN_TRACES_NS = BigInt(
+ 5 * TIME_UNIT_TO_NANO.m,
+ ); // 5m
+ static readonly MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET = BigInt(
+ 5 * TIME_UNIT_TO_NANO.s,
+ ); // 5s
+ static readonly REAL_TIME_TRACES_WITHOUT_RTE_OFFSET = [
+ TraceType.CUJS,
+ TraceType.EVENT_LOG,
+ ];
private legacyParsers = new Map<TraceType, FileAndParser>();
private perfettoParsers = new Map<TraceType, FileAndParser>();
@@ -40,7 +49,14 @@
if (perfettoParsers) {
this.addPerfettoParsers(perfettoParsers, userNotificationsListener);
}
-
+ // Traces were simultaneously upgraded to contain real-to-boottime or real-to-monotonic offsets.
+ // If we have a mix of parsers with and without offsets, the ones without must be dangling
+ // trace files with old data, and should be filtered out.
+ legacyParsers = this.filterOutParsersWithoutOffsetsIfRequired(
+ legacyParsers,
+ perfettoParsers,
+ userNotificationsListener,
+ );
legacyParsers = this.filterOutLegacyParsersWithOldData(
legacyParsers,
userNotificationsListener,
@@ -100,21 +116,6 @@
return await FileUtils.createZipArchive(uniqueArchiveFiles);
}
- findCommonTimestampType(): TimestampType | undefined {
- return this.findCommonTimestampTypeInternal(this.getParsers());
- }
- private findCommonTimestampTypeInternal(
- parsers: Array<Parser<object>>,
- ): TimestampType | undefined {
- const priorityOrder = [TimestampType.REAL, TimestampType.ELAPSED];
- for (const type of priorityOrder) {
- if (parsers.every((parser) => parser.getTimestamps(type) !== undefined)) {
- return type;
- }
- }
- return undefined;
- }
-
private addLegacyParsers(
parsers: FileAndParser[],
userNotificationsListener: UserNotificationsListener,
@@ -211,22 +212,58 @@
newLegacyParsers: FileAndParser[],
userNotificationsListener: UserNotificationsListener,
): FileAndParser[] {
- const allParsers = [
+ let allParsers = [
...newLegacyParsers,
...this.legacyParsers.values(),
...this.perfettoParsers.values(),
];
- const commonTimestampType = this.findCommonTimestampTypeInternal(
- allParsers.map(({parser}) => parser),
+ const latestMonotonicOffset = this.getLatestRealToMonotonicOffset(
+ allParsers.map(({parser, file}) => parser),
);
- if (commonTimestampType === undefined) {
- return newLegacyParsers;
- }
+ const latestBootTimeOffset = this.getLatestRealToBootTimeOffset(
+ allParsers.map(({parser, file}) => parser),
+ );
+
+ newLegacyParsers = newLegacyParsers.filter(({parser, file}) => {
+ const monotonicOffset = parser.getRealToMonotonicTimeOffsetNs();
+ if (monotonicOffset && latestMonotonicOffset) {
+ const isOldData =
+ Math.abs(Number(monotonicOffset - latestMonotonicOffset)) >
+ LoadedParsers.MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET;
+ if (isOldData) {
+ userNotificationsListener.onNotifications([
+ new TraceHasOldData(file.getDescriptor()),
+ ]);
+ return false;
+ }
+ }
+
+ const bootTimeOffset = parser.getRealToBootTimeOffsetNs();
+ if (bootTimeOffset && latestBootTimeOffset) {
+ const isOldData =
+ Math.abs(Number(bootTimeOffset - latestBootTimeOffset)) >
+ LoadedParsers.MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET;
+ if (isOldData) {
+ userNotificationsListener.onNotifications([
+ new TraceHasOldData(file.getDescriptor()),
+ ]);
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ allParsers = [
+ ...newLegacyParsers,
+ ...this.legacyParsers.values(),
+ ...this.perfettoParsers.values(),
+ ];
const timeRanges = allParsers
.map(({parser}) => {
- const timestamps = parser.getTimestamps(commonTimestampType);
+ const timestamps = parser.getTimestamps();
if (!timestamps || timestamps.length === 0) {
return undefined;
}
@@ -246,7 +283,7 @@
return true;
}
- const timestamps = parser.getTimestamps(commonTimestampType);
+ const timestamps = parser.getTimestamps();
if (!timestamps || timestamps.length === 0) {
return true;
}
@@ -320,6 +357,50 @@
return newLegacyParsers;
}
+ private filterOutParsersWithoutOffsetsIfRequired(
+ newLegacyParsers: FileAndParser[],
+ perfettoParsers: FileAndParsers | undefined,
+ userNotificationsListener: UserNotificationsListener,
+ ): FileAndParser[] {
+ const hasParserWithOffset =
+ perfettoParsers ||
+ newLegacyParsers.find(({parser, file}) => {
+ return (
+ parser.getRealToBootTimeOffsetNs() !== undefined ||
+ parser.getRealToMonotonicTimeOffsetNs() !== undefined
+ );
+ });
+ const hasParserWithoutOffset = newLegacyParsers.find(({parser, file}) => {
+ return (
+ parser.getRealToBootTimeOffsetNs() === undefined &&
+ parser.getRealToMonotonicTimeOffsetNs() === undefined
+ );
+ });
+
+ if (hasParserWithOffset && hasParserWithoutOffset) {
+ return newLegacyParsers.filter(({parser, file}) => {
+ if (
+ LoadedParsers.REAL_TIME_TRACES_WITHOUT_RTE_OFFSET.some(
+ (traceType) => parser.getTraceType() === traceType,
+ )
+ ) {
+ return true;
+ }
+ const hasOffset =
+ parser.getRealToMonotonicTimeOffsetNs() !== undefined ||
+ parser.getRealToBootTimeOffsetNs() !== undefined;
+ if (!hasOffset) {
+ userNotificationsListener.onNotifications([
+ new TraceHasOldData(parser.getDescriptors().join()),
+ ]);
+ }
+ return hasOffset;
+ });
+ }
+
+ return newLegacyParsers;
+ }
+
private findLastTimeGapAboveThreshold(
ranges: readonly TimeRange[],
): TimeRange | undefined {
@@ -338,4 +419,34 @@
return undefined;
}
+
+ getLatestRealToMonotonicOffset(
+ parsers: Array<Parser<object>>,
+ ): bigint | undefined {
+ const p = parsers
+ .filter((offset) => offset.getRealToMonotonicTimeOffsetNs() !== undefined)
+ .sort((a, b) => {
+ return Number(
+ (a.getRealToMonotonicTimeOffsetNs() ?? 0n) -
+ (b.getRealToMonotonicTimeOffsetNs() ?? 0n),
+ );
+ })
+ .at(-1);
+ return p?.getRealToMonotonicTimeOffsetNs();
+ }
+
+ getLatestRealToBootTimeOffset(
+ parsers: Array<Parser<object>>,
+ ): bigint | undefined {
+ const p = parsers
+ .filter((offset) => offset.getRealToBootTimeOffsetNs() !== undefined)
+ .sort((a, b) => {
+ return Number(
+ (a.getRealToBootTimeOffsetNs() ?? 0n) -
+ (b.getRealToBootTimeOffsetNs() ?? 0n),
+ );
+ })
+ .at(-1);
+ return p?.getRealToBootTimeOffsetNs();
+ }
}
diff --git a/tools/winscope/src/app/loaded_parsers_test.ts b/tools/winscope/src/app/loaded_parsers_test.ts
index f2bf1b6..4944835 100644
--- a/tools/winscope/src/app/loaded_parsers_test.ts
+++ b/tools/winscope/src/app/loaded_parsers_test.ts
@@ -15,34 +15,41 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimeRange, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimeRange} from 'common/time';
import {UserWarning} from 'messaging/user_warning';
import {TraceHasOldData, TraceOverridden} from 'messaging/user_warnings';
import {FileAndParser} from 'parsers/file_and_parser';
import {FileAndParsers} from 'parsers/file_and_parsers';
import {ParserBuilder} from 'test/unit/parser_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {Parser} from 'trace/parser';
import {TraceFile} from 'trace/trace_file';
import {TraceType} from 'trace/trace_type';
import {LoadedParsers} from './loaded_parsers';
describe('LoadedParsers', () => {
- const realZeroTimestamp = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
- const elapsedZeroTimestamp =
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n);
+ const realZeroTimestamp = TimestampConverterUtils.makeRealTimestamp(0n);
+ const elapsedZeroTimestamp = TimestampConverterUtils.makeElapsedTimestamp(0n);
const oldTimestamps = [
realZeroTimestamp,
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(2n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(3n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(4n),
+ TimestampConverterUtils.makeRealTimestamp(1n),
+ TimestampConverterUtils.makeRealTimestamp(2n),
+ TimestampConverterUtils.makeRealTimestamp(3n),
+ TimestampConverterUtils.makeRealTimestamp(4n),
+ ];
+
+ const elapsedTimestamps = [
+ elapsedZeroTimestamp,
+ TimestampConverterUtils.makeElapsedTimestamp(1n),
+ TimestampConverterUtils.makeElapsedTimestamp(2n),
+ TimestampConverterUtils.makeElapsedTimestamp(3n),
+ TimestampConverterUtils.makeElapsedTimestamp(4n),
];
const timestamps = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n * 60n * 1000000000n + 10n), // 5m10ns
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n * 60n * 1000000000n + 11n), // 5m11ns
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n * 60n * 1000000000n + 12n), // 5m12ns
+ TimestampConverterUtils.makeRealTimestamp(5n * 60n * 1000000000n + 10n), // 5m10ns
+ TimestampConverterUtils.makeRealTimestamp(5n * 60n * 1000000000n + 11n), // 5m11ns
+ TimestampConverterUtils.makeRealTimestamp(5n * 60n * 1000000000n + 12n), // 5m12ns
];
const filename = 'filename';
@@ -68,6 +75,12 @@
.setTimestamps([])
.setDescriptors([filename])
.build();
+ const parserSf_elapsed = new ParserBuilder<object>()
+ .setType(TraceType.SURFACE_FLINGER)
+ .setTimestamps(elapsedTimestamps)
+ .setDescriptors([filename])
+ .setNoOffsets(true)
+ .build();
const parserWm0 = new ParserBuilder<object>()
.setType(TraceType.WINDOW_MANAGER)
.setTimestamps(timestamps)
@@ -83,6 +96,12 @@
.setTimestamps([realZeroTimestamp])
.setDescriptors([filename])
.build();
+ const parserWm_elapsed = new ParserBuilder<object>()
+ .setType(TraceType.WINDOW_MANAGER)
+ .setTimestamps(elapsedTimestamps)
+ .setDescriptors([filename])
+ .setNoOffsets(true)
+ .build();
const parserWmTransitions = new ParserBuilder<object>()
.setType(TraceType.WM_TRANSITION)
.setTimestamps([
@@ -92,6 +111,12 @@
])
.setDescriptors([filename])
.build();
+ const parserEventlog = new ParserBuilder<object>()
+ .setType(TraceType.EVENT_LOG)
+ .setTimestamps(timestamps)
+ .setDescriptors([filename])
+ .setNoOffsets(true)
+ .build();
let loadedParsers: LoadedParsers;
let warnings: UserWarning[] = [];
@@ -132,13 +157,26 @@
expectLoadResult([parserWm0], [new TraceOverridden(filename)]);
});
+ it('drops elapsed-only parsers if parsers with real timestamps present', () => {
+ loadParsers([parserSf_elapsed, parserSf0], []);
+ expectLoadResult([parserSf0], [new TraceHasOldData(filename)]);
+ });
+
+ it('doesnt drop elapsed-only parsers if no parsers with real timestamps present', () => {
+ loadParsers([parserSf_elapsed, parserWm_elapsed], []);
+ expectLoadResult([parserSf_elapsed, parserWm_elapsed], []);
+ });
+
+ it('keeps real-time parsers without offset', () => {
+ loadParsers([parserSf0, parserEventlog], []);
+ expectLoadResult([parserSf0, parserEventlog], []);
+ });
+
describe('drops legacy parser with old data (dangling old trace file)', () => {
const timeGapFrom = assertDefined(
- parserSf_longButOldData.getTimestamps(TimestampType.REAL)?.at(-1),
+ parserSf_longButOldData.getTimestamps()?.at(-1),
);
- const timeGapTo = assertDefined(
- parserWm0.getTimestamps(TimestampType.REAL)?.at(0),
- );
+ const timeGapTo = assertDefined(parserWm0.getTimestamps()?.at(0));
const timeGap = new TimeRange(timeGapFrom, timeGapTo);
it('taking into account other legacy parsers', () => {
@@ -174,9 +212,7 @@
it('is robust to traces with time range overlap', () => {
const parser = parserSf0;
- const timestamps = assertDefined(
- parserSf0.getTimestamps(TimestampType.REAL),
- );
+ const timestamps = assertDefined(parserSf0.getTimestamps());
const timestampsOverlappingFront = [
timestamps[0].add(-1n),
diff --git a/tools/winscope/src/app/mediator.ts b/tools/winscope/src/app/mediator.ts
index 824ac9e..0b6c76b 100644
--- a/tools/winscope/src/app/mediator.ts
+++ b/tools/winscope/src/app/mediator.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import {Timestamp} from 'common/time';
import {TimeUtils} from 'common/time_utils';
+import {CrossToolProtocol} from 'cross_tool/cross_tool_protocol';
import {Analytics} from 'logging/analytics';
import {ProgressListener} from 'messaging/progress_listener';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
@@ -41,7 +41,7 @@
export class Mediator {
private abtChromeExtensionProtocol: WinscopeEventEmitter &
WinscopeEventListener;
- private crossToolProtocol: WinscopeEventEmitter & WinscopeEventListener;
+ private crossToolProtocol: CrossToolProtocol;
private uploadTracesComponent?: ProgressListener;
private collectTracesComponent?: ProgressListener & WinscopeEventListener;
private traceViewComponent?: WinscopeEventEmitter & WinscopeEventListener;
@@ -55,14 +55,14 @@
private viewers: Viewer[] = [];
private focusedTabView: undefined | View;
private areViewersLoaded = false;
- private lastRemoteToolTimestampReceived: Timestamp | undefined;
+ private lastRemoteToolRealNsReceived: bigint | undefined;
private currentProgressListener?: ProgressListener;
constructor(
tracePipeline: TracePipeline,
timelineData: TimelineData,
abtChromeExtensionProtocol: WinscopeEventEmitter & WinscopeEventListener,
- crossToolProtocol: WinscopeEventEmitter & WinscopeEventListener,
+ crossToolProtocol: CrossToolProtocol,
appComponent: WinscopeEventListener,
userNotificationsListener: UserNotificationsListener,
storage: Storage,
@@ -254,12 +254,8 @@
});
if (!omitCrossToolProtocol) {
- const utcTimestamp = position.timestamp.toUTC();
- const utcPosition = position.entry
- ? TracePosition.fromTraceEntry(position.entry, utcTimestamp)
- : TracePosition.fromTimestamp(utcTimestamp);
- const utcEvent = new TracePositionUpdate(utcPosition);
- promises.push(this.crossToolProtocol.onWinscopeEvent(utcEvent));
+ const event = new TracePositionUpdate(position);
+ promises.push(this.crossToolProtocol.onWinscopeEvent(event));
}
await Promise.all(promises);
@@ -285,23 +281,21 @@
}
private async processRemoteToolTimestampReceived(timestampNs: bigint) {
- const factory = this.tracePipeline.getTimestampFactory();
- const timestamp = factory.makeRealTimestamp(timestampNs);
- this.lastRemoteToolTimestampReceived = timestamp;
+ const timestamp = this.tracePipeline
+ .getTimestampConverter()
+ .tryMakeTimestampFromRealNs(timestampNs);
+ if (timestamp === undefined) {
+ console.warn(
+ 'Cannot apply new timestamp received from remote tool, as Winscope is only accepting elapsed timestamps for the loaded traces.',
+ );
+ return;
+ }
+ this.lastRemoteToolRealNsReceived = timestamp.getValueNs();
if (!this.areViewersLoaded) {
return; // apply timestamp later when traces are visualized
}
- if (this.timelineData.getTimestampType() !== timestamp.getType()) {
- console.warn(
- 'Cannot apply new timestamp received from remote tool.' +
- ` Remote tool notified timestamp type ${timestamp.getType()},` +
- ` but Winscope is accepting timestamp type ${this.timelineData.getTimestampType()}.`,
- );
- return;
- }
-
const position = this.timelineData.makePositionFromActiveTrace(timestamp);
this.timelineData.setPosition(position);
@@ -343,6 +337,11 @@
await this.timelineData.initialize(
this.tracePipeline.getTraces(),
await this.tracePipeline.getScreenRecordingVideo(),
+ this.tracePipeline.getTimestampConverter(),
+ );
+
+ this.crossToolProtocol.setTimestampConverter(
+ this.tracePipeline.getTimestampConverter(),
);
this.viewers = new ViewerFactory().createViewers(
@@ -384,14 +383,15 @@
}
private getInitialTracePosition(): TracePosition | undefined {
- if (
- this.lastRemoteToolTimestampReceived &&
- this.timelineData.getTimestampType() ===
- this.lastRemoteToolTimestampReceived.getType()
- ) {
- return this.timelineData.makePositionFromActiveTrace(
- this.lastRemoteToolTimestampReceived,
- );
+ if (this.lastRemoteToolRealNsReceived !== undefined) {
+ const lastRemoteToolTimestamp = this.tracePipeline
+ .getTimestampConverter()
+ .tryMakeTimestampFromRealNs(this.lastRemoteToolRealNsReceived);
+ if (lastRemoteToolTimestamp) {
+ return this.timelineData.makePositionFromActiveTrace(
+ lastRemoteToolTimestamp,
+ );
+ }
}
const position = this.timelineData.getCurrentPosition();
@@ -426,8 +426,9 @@
this.timelineData.clear();
this.viewers = [];
this.areViewersLoaded = false;
- this.lastRemoteToolTimestampReceived = undefined;
+ this.lastRemoteToolRealNsReceived = undefined;
this.focusedTabView = undefined;
+ this.crossToolProtocol?.setTimestampConverter(undefined);
await this.appComponent.onWinscopeEvent(new ViewersUnloaded());
}
diff --git a/tools/winscope/src/app/mediator_test.ts b/tools/winscope/src/app/mediator_test.ts
index 99583ab..56a06a4 100644
--- a/tools/winscope/src/app/mediator_test.ts
+++ b/tools/winscope/src/app/mediator_test.ts
@@ -16,10 +16,9 @@
import {assertDefined} from 'common/assert_utils';
import {FunctionUtils} from 'common/function_utils';
-import {
- NO_TIMEZONE_OFFSET_FACTORY,
- TimestampFactory,
-} from 'common/timestamp_factory';
+import {TimezoneInfo} from 'common/time';
+import {TimestampConverter} from 'common/timestamp_converter';
+import {CrossToolProtocol} from 'cross_tool/cross_tool_protocol';
import {ProgressListener} from 'messaging/progress_listener';
import {ProgressListenerStub} from 'messaging/progress_listener_stub';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
@@ -46,6 +45,7 @@
import {WinscopeEventListener} from 'messaging/winscope_event_listener';
import {WinscopeEventListenerStub} from 'messaging/winscope_event_listener_stub';
import {MockStorage} from 'test/unit/mock_storage';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -75,7 +75,7 @@
let tracePipeline: TracePipeline;
let timelineData: TimelineData;
let abtChromeExtensionProtocol: WinscopeEventEmitter & WinscopeEventListener;
- let crossToolProtocol: WinscopeEventEmitter & WinscopeEventListener;
+ let crossToolProtocol: CrossToolProtocol;
let appComponent: WinscopeEventListener;
let timelineComponent: WinscopeEventEmitter & WinscopeEventListener;
let uploadTracesComponent: ProgressListenerStub;
@@ -87,8 +87,8 @@
const viewers = [viewerStub0, viewerStub1, viewerOverlay];
let tracePositionUpdateListeners: WinscopeEventListener[];
- const TIMESTAMP_10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const TIMESTAMP_11 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n);
+ const TIMESTAMP_10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const TIMESTAMP_11 = TimestampConverterUtils.makeRealTimestamp(11n);
const POSITION_10 = TracePosition.fromTimestamp(TIMESTAMP_10);
const POSITION_11 = TracePosition.fromTimestamp(TIMESTAMP_11);
@@ -116,10 +116,7 @@
new WinscopeEventEmitterStub(),
new WinscopeEventListenerStub(),
);
- crossToolProtocol = FunctionUtils.mixin(
- new WinscopeEventEmitterStub(),
- new WinscopeEventListenerStub(),
- );
+ crossToolProtocol = new CrossToolProtocol();
appComponent = new WinscopeEventListenerStub();
timelineComponent = FunctionUtils.mixin(
new WinscopeEventEmitterStub(),
@@ -270,19 +267,20 @@
});
it('propagates trace position update according to timezone', async () => {
- const timezoneInfo = {
+ const timezoneInfo: TimezoneInfo = {
timezone: 'Asia/Kolkata',
locale: 'en-US',
+ utcOffsetMs: 19800000,
};
- const factory = new TimestampFactory(timezoneInfo);
- spyOn(tracePipeline, 'getTimestampFactory').and.returnValue(factory);
+ const converter = new TimestampConverter(timezoneInfo, 0n);
+ spyOn(tracePipeline, 'getTimestampConverter').and.returnValue(converter);
await loadFiles();
await loadTraceView();
// notify position
resetSpyCalls();
const expectedPosition = TracePosition.fromTimestamp(
- factory.makeRealTimestamp(10n),
+ converter.makeTimestampFromRealNs(10n),
);
await mediator.onWinscopeEvent(new TracePositionUpdate(expectedPosition));
checkTracePositionUpdateEvents(
@@ -300,7 +298,7 @@
resetSpyCalls();
const finalTimestampNs = timelineData.getFullTimeRange().to.getValueNs();
const timestamp =
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(finalTimestampNs);
+ TimestampConverterUtils.makeRealTimestamp(finalTimestampNs);
const position = TracePosition.fromTimestamp(timestamp);
await mediator.onWinscopeEvent(new TracePositionUpdate(position, true));
@@ -337,6 +335,7 @@
describe('timestamp received from remote tool', () => {
it('propagates trace position update', async () => {
+ tracePipeline.getTimestampConverter().setRealToMonotonicTimeOffsetNs(0n);
await loadFiles();
await loadTraceView();
@@ -362,12 +361,13 @@
});
it('propagates trace position update according to timezone', async () => {
- const timezoneInfo = {
+ const timezoneInfo: TimezoneInfo = {
timezone: 'Asia/Kolkata',
locale: 'en-US',
+ utcOffsetMs: 19800000,
};
- const factory = new TimestampFactory(timezoneInfo);
- spyOn(tracePipeline, 'getTimestampFactory').and.returnValue(factory);
+ const converter = new TimestampConverter(timezoneInfo, 0n);
+ spyOn(tracePipeline, 'getTimestampConverter').and.returnValue(converter);
await loadFiles();
await loadTraceView();
@@ -375,7 +375,7 @@
resetSpyCalls();
const expectedPosition = TracePosition.fromTimestamp(
- factory.makeRealTimestamp(10n),
+ converter.makeTimestampFromRealNs(10n),
);
await mediator.onWinscopeEvent(new RemoteToolTimestampReceived(10n));
checkTracePositionUpdateEvents(
@@ -385,6 +385,7 @@
});
it("doesn't propagate timestamp back to remote tool", async () => {
+ tracePipeline.getTimestampConverter().setRealToMonotonicTimeOffsetNs(0n);
await loadFiles();
await loadTraceView();
@@ -401,6 +402,8 @@
});
it('defers trace position propagation till traces are loaded and visualized', async () => {
+ // ensure converter has been used to create real timestamps
+ tracePipeline.getTimestampConverter().makeTimestampFromRealNs(0n);
// keep timestamp for later
await mediator.onWinscopeEvent(
new RemoteToolTimestampReceived(TIMESTAMP_10.getValueNs()),
diff --git a/tools/winscope/src/app/timeline_data.ts b/tools/winscope/src/app/timeline_data.ts
index 799eaf1..5a1c891 100644
--- a/tools/winscope/src/app/timeline_data.ts
+++ b/tools/winscope/src/app/timeline_data.ts
@@ -15,12 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {
- INVALID_TIME_NS,
- TimeRange,
- Timestamp,
- TimestampType,
-} from 'common/time';
+import {INVALID_TIME_NS, TimeRange, Timestamp} from 'common/time';
+import {ComponentTimestampConverter} from 'common/timestamp_converter';
import {TimestampUtils} from 'common/timestamp_utils';
import {ScreenRecordingUtils} from 'trace/screen_recording_utils';
import {Trace, TraceEntry} from 'trace/trace';
@@ -33,7 +29,6 @@
export class TimelineData {
private traces = new Traces();
private screenRecordingVideo?: Blob;
- private timestampType?: TimestampType;
private firstEntry?: TraceEntry<{}>;
private lastEntry?: TraceEntry<{}>;
private explicitlySetPosition?: TracePosition;
@@ -47,10 +42,17 @@
>();
private activeViewTraceTypes: TraceType[] = []; // dependencies of current active view
private transitions: PropertyTreeNode[] = []; // cached trace entries to avoid TP and object creation latencies each time transition timeline is redrawn
+ private timestampConverter: ComponentTimestampConverter | undefined;
- async initialize(traces: Traces, screenRecordingVideo: Blob | undefined) {
+ async initialize(
+ traces: Traces,
+ screenRecordingVideo: Blob | undefined,
+ timestampConverter: ComponentTimestampConverter,
+ ) {
this.clear();
+ this.timestampConverter = timestampConverter;
+
this.traces = new Traces();
traces.forEachTrace((trace, type) => {
// Filter out dumps with invalid timestamp (would mess up the timeline)
@@ -74,7 +76,6 @@
this.screenRecordingVideo = screenRecordingVideo;
this.firstEntry = this.findFirstEntry();
this.lastEntry = this.findLastEntry();
- this.timestampType = this.firstEntry?.getTimestamp().getType();
const types = traces
.mapTrace((trace, type) => type)
@@ -93,6 +94,10 @@
return this.transitions;
}
+ getTimestampConverter(): ComponentTimestampConverter | undefined {
+ return this.timestampConverter;
+ }
+
getCurrentPosition(): TracePosition | undefined {
if (this.explicitlySetPosition) {
return this.explicitlySetPosition;
@@ -127,19 +132,6 @@
return;
}
- if (position) {
- if (this.timestampType === undefined) {
- throw Error(
- 'Attempted to set explicit position but no timestamp type is available',
- );
- }
- if (position.timestamp.getType() !== this.timestampType) {
- throw Error(
- 'Attempted to set explicit position with incompatible timestamp type',
- );
- }
- }
-
this.explicitlySetPosition = position;
}
@@ -165,10 +157,6 @@
this.activeViewTraceTypes = types;
}
- getTimestampType(): TimestampType | undefined {
- return this.timestampType;
- }
-
getFullTimeRange(): TimeRange {
if (!this.firstEntry || !this.lastEntry) {
throw Error('Trying to get full time range when there are no timestamps');
@@ -239,8 +227,8 @@
}
return ScreenRecordingUtils.timestampToVideoTimeSeconds(
- firstTimestamp,
- entry.getTimestamp(),
+ firstTimestamp.getValueNs(),
+ entry.getTimestamp().getValueNs(),
);
}
@@ -328,7 +316,6 @@
this.firstEntry = undefined;
this.lastEntry = undefined;
this.explicitlySetPosition = undefined;
- this.timestampType = undefined;
this.explicitlySetSelection = undefined;
this.lastReturnedCurrentPosition = undefined;
this.screenRecordingVideo = undefined;
diff --git a/tools/winscope/src/app/timeline_data_test.ts b/tools/winscope/src/app/timeline_data_test.ts
index 87b625f..6e2eae1 100644
--- a/tools/winscope/src/app/timeline_data_test.ts
+++ b/tools/winscope/src/app/timeline_data_test.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
@@ -24,11 +24,11 @@
describe('TimelineData', () => {
let timelineData: TimelineData;
- const timestamp0 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
- const timestamp5 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n);
- const timestamp9 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(9n);
- const timestamp10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const timestamp11 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n);
+ const timestamp0 = TimestampConverterUtils.makeRealTimestamp(0n);
+ const timestamp5 = TimestampConverterUtils.makeRealTimestamp(5n);
+ const timestamp9 = TimestampConverterUtils.makeRealTimestamp(9n);
+ const timestamp10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const timestamp11 = TimestampConverterUtils.makeRealTimestamp(11n);
const traces = new TracesBuilder()
.setTimestamps(TraceType.PROTO_LOG, [timestamp9])
@@ -47,7 +47,7 @@
assertDefined(traces.getTrace(TraceType.WINDOW_MANAGER)).getEntry(0),
);
const position1000 = TracePosition.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1000n),
+ TimestampConverterUtils.makeRealTimestamp(1000n),
);
beforeEach(() => {
@@ -57,7 +57,11 @@
it('can be initialized', () => {
expect(timelineData.getCurrentPosition()).toBeUndefined();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getCurrentPosition()).toBeDefined();
});
@@ -68,7 +72,11 @@
.build();
it('drops trace if it is a dump (will not display in timeline UI)', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(
timelineData.getTraces().getTrace(TraceType.WINDOW_MANAGER),
).toBeUndefined();
@@ -77,7 +85,11 @@
});
it('is robust to prev/next entry request of a dump', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(
timelineData.getPreviousEntryFor(TraceType.WINDOW_MANAGER),
).toBeUndefined();
@@ -88,12 +100,20 @@
});
it('uses first entry of first active trace by default', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getCurrentPosition()).toEqual(position10);
});
it('uses explicit position if set', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getCurrentPosition()).toEqual(position10);
timelineData.setPosition(position1000);
@@ -107,7 +127,11 @@
});
it('sets active trace types and update current position accordingly', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
timelineData.setActiveViewTraceTypes([]);
expect(timelineData.getCurrentPosition()).toEqual(position9);
@@ -131,7 +155,11 @@
// no trace
{
const traces = new TracesBuilder().build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasTimestamps()).toBeFalse();
}
// trace without timestamps
@@ -139,7 +167,11 @@
const traces = new TracesBuilder()
.setTimestamps(TraceType.SURFACE_FLINGER, [])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasTimestamps()).toBeFalse();
}
// trace with timestamps
@@ -147,7 +179,11 @@
const traces = new TracesBuilder()
.setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasTimestamps()).toBeTrue();
}
});
@@ -158,7 +194,11 @@
// no trace
{
const traces = new TracesBuilder().build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse();
}
// no distinct timestamps
@@ -167,7 +207,11 @@
.setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp10])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeFalse();
}
// distinct timestamps
@@ -176,13 +220,21 @@
.setTimestamps(TraceType.SURFACE_FLINGER, [timestamp10])
.setTimestamps(TraceType.WINDOW_MANAGER, [timestamp11])
.build();
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.hasMoreThanOneDistinctTimestamp()).toBeTrue();
}
});
it('getCurrentPosition() returns same object if no change to range', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getCurrentPosition()).toBe(
timelineData.getCurrentPosition(),
@@ -196,8 +248,12 @@
});
it('makePositionFromActiveTrace()', () => {
- timelineData.initialize(traces, undefined);
- const time100 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
+ const time100 = TimestampConverterUtils.makeRealTimestamp(100n);
{
timelineData.setActiveViewTraceTypes([TraceType.SURFACE_FLINGER]);
@@ -219,7 +275,11 @@
});
it('getFullTimeRange() returns same object if no change to range', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getFullTimeRange()).toBe(
timelineData.getFullTimeRange(),
@@ -227,7 +287,11 @@
});
it('getSelectionTimeRange() returns same object if no change to range', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getSelectionTimeRange()).toBe(
timelineData.getSelectionTimeRange(),
@@ -244,7 +308,11 @@
});
it('getZoomRange() returns same object if no change to range', () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
expect(timelineData.getZoomRange()).toBe(timelineData.getZoomRange());
@@ -257,7 +325,11 @@
});
it("getCurrentPosition() prioritizes active trace's first entry", () => {
- timelineData.initialize(traces, undefined);
+ timelineData.initialize(
+ traces,
+ undefined,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
+ );
timelineData.setActiveViewTraceTypes([TraceType.WINDOW_MANAGER]);
expect(timelineData.getCurrentPosition()?.timestamp).toBe(timestamp11);
diff --git a/tools/winscope/src/app/trace_file_filter.ts b/tools/winscope/src/app/trace_file_filter.ts
index 91a1dc8..45d187e 100644
--- a/tools/winscope/src/app/trace_file_filter.ts
+++ b/tools/winscope/src/app/trace_file_filter.ts
@@ -50,9 +50,6 @@
const bugreportMainEntry = files.find((file) =>
file.file.name.endsWith('main_entry.txt'),
);
- const bugReportDumpstateBoard = files.find((file) =>
- file.file.name.endsWith('dumpstate_board.txt'),
- );
const perfettoFiles = files.filter((file) => this.isPerfettoFile(file));
const legacyFiles = files.filter((file) => !this.isPerfettoFile(file));
@@ -67,8 +64,9 @@
};
}
- const timezoneInfo = await this.processDumpstateBoard(
- bugReportDumpstateBoard,
+ const timezoneInfo = await this.processRawBugReport(
+ assertDefined(bugreportMainEntry),
+ files,
);
return await this.filterBugreport(
@@ -79,36 +77,40 @@
);
}
- private async processDumpstateBoard(
- bugReportDumpstateBoard: TraceFile | undefined,
+ private async processRawBugReport(
+ bugreportMainEntry: TraceFile,
+ files: TraceFile[],
): Promise<TimezoneInfo | undefined> {
- if (!bugReportDumpstateBoard) {
+ const bugreportName = (await bugreportMainEntry.file.text()).trim();
+ const rawBugReport = files.find((file) => file.file.name === bugreportName);
+ if (!rawBugReport) {
return undefined;
}
- const traceBuffer = new Uint8Array(
- await bugReportDumpstateBoard.file.arrayBuffer(),
- );
+ const traceBuffer = new Uint8Array(await rawBugReport.file.arrayBuffer());
const fileData = new TextDecoder().decode(traceBuffer);
- const localeStartIndex = fileData.indexOf('[persist.sys.locale]');
- const timezoneStartIndex = fileData.indexOf('[persist.sys.timezone]');
- if (localeStartIndex === -1 || timezoneStartIndex === -1) {
+ const timezoneStartIndex = fileData.indexOf('[persist.sys.timezone]');
+ if (timezoneStartIndex === -1) {
return undefined;
}
-
- const locale = this.extractValueFromDumpstateBoard(
- fileData,
- localeStartIndex,
- );
- const timezone = this.extractValueFromDumpstateBoard(
+ const timezone = this.extractValueFromRawBugReport(
fileData,
timezoneStartIndex,
);
- return {timezone, locale};
+
+ let utcOffsetMs = undefined;
+ const timeOffsetIndex = fileData.indexOf('[persist.sys.time.offset]');
+ if (timeOffsetIndex !== -1) {
+ utcOffsetMs = Number(
+ this.extractValueFromRawBugReport(fileData, timeOffsetIndex),
+ );
+ }
+
+ return {timezone, locale: 'en-US', utcOffsetMs};
}
- private extractValueFromDumpstateBoard(
+ private extractValueFromRawBugReport(
fileData: string,
startIndex: number,
): string {
diff --git a/tools/winscope/src/app/trace_file_filter_test.ts b/tools/winscope/src/app/trace_file_filter_test.ts
index 6ef8319..d259844 100644
--- a/tools/winscope/src/app/trace_file_filter_test.ts
+++ b/tools/winscope/src/app/trace_file_filter_test.ts
@@ -132,7 +132,7 @@
expect(warnings).toEqual([]);
});
- it('identifies dumpstate_board.txt file', async () => {
+ it('identifies timezone information from bugreport codename file', async () => {
const legacyFile = makeTraceFile(
'proto/window_CRITICAL.proto',
bugreportArchive,
@@ -140,7 +140,6 @@
const bugreportFiles = [
await makeBugreportMainEntryTraceFile(),
await makeBugreportCodenameTraceFile(),
- await makeBugreportDumpstateBoardTextFile(),
legacyFile,
];
const result = await filter.filter(bugreportFiles, notificationListener);
@@ -149,6 +148,28 @@
expect(result.timezoneInfo).toEqual({
timezone: 'Asia/Kolkata',
locale: 'en-US',
+ utcOffsetMs: 19800000,
+ });
+ expect(warnings).toEqual([]);
+ });
+
+ it('identifies timezone information from bugreport codename file without time offset', async () => {
+ const legacyFile = makeTraceFile(
+ 'proto/window_CRITICAL.proto',
+ bugreportArchive,
+ );
+ const bugreportFiles = [
+ await makeBugreportMainEntryTraceFile(),
+ await makeBugreportCodenameNoTimeOffsetTraceFile(),
+ legacyFile,
+ ];
+ const result = await filter.filter(bugreportFiles, notificationListener);
+ expect(result.legacy).toEqual([legacyFile]);
+ expect(result.perfetto).toBeUndefined();
+ expect(result.timezoneInfo).toEqual({
+ timezone: 'Asia/Kolkata',
+ locale: 'en-US',
+ utcOffsetMs: undefined,
});
expect(warnings).toEqual([]);
});
@@ -233,13 +254,13 @@
filename: string,
parentArchive?: File,
size?: number,
- ) {
+ ): TraceFile {
size = size ?? 0;
const file = new File([new ArrayBuffer(size)], filename);
return new TraceFile(file as unknown as File, parentArchive);
}
- async function makeBugreportMainEntryTraceFile() {
+ async function makeBugreportMainEntryTraceFile(): Promise<TraceFile> {
const file = await UnitTestUtils.getFixtureFile(
'bugreports/main_entry.txt',
'main_entry.txt',
@@ -247,15 +268,7 @@
return new TraceFile(file, bugreportArchive);
}
- async function makeBugreportDumpstateBoardTextFile() {
- const file = await UnitTestUtils.getFixtureFile(
- 'bugreports/dumpstate_board.txt',
- 'dumpstate_board.txt',
- );
- return new TraceFile(file, bugreportArchive);
- }
-
- async function makeBugreportCodenameTraceFile() {
+ async function makeBugreportCodenameTraceFile(): Promise<TraceFile> {
const file = await UnitTestUtils.getFixtureFile(
'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
@@ -263,11 +276,19 @@
return new TraceFile(file, bugreportArchive);
}
- async function makeZippedTraceFile() {
+ async function makeZippedTraceFile(): Promise<TraceFile> {
const file = await UnitTestUtils.getFixtureFile(
'traces/winscope.zip',
'FS/data/misc/wmtrace/winscope.zip',
);
return new TraceFile(file, bugreportArchive);
}
+
+ async function makeBugreportCodenameNoTimeOffsetTraceFile(): Promise<TraceFile> {
+ const file = await UnitTestUtils.getFixtureFile(
+ 'bugreports/bugreport-codename_beta-no-time-offset-UPB2.230407.019-2023-05-30-14-33-48.txt',
+ 'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
+ );
+ return new TraceFile(file, bugreportArchive);
+ }
});
diff --git a/tools/winscope/src/app/trace_pipeline.ts b/tools/winscope/src/app/trace_pipeline.ts
index c7d7842..db5eb94 100644
--- a/tools/winscope/src/app/trace_pipeline.ts
+++ b/tools/winscope/src/app/trace_pipeline.ts
@@ -15,18 +15,15 @@
*/
import {FileUtils} from 'common/file_utils';
+import {INVALID_TIME_NS} from 'common/time';
import {
- NO_TIMEZONE_OFFSET_FACTORY,
- TimestampFactory,
-} from 'common/timestamp_factory';
+ TimestampConverter,
+ UTC_TIMEZONE_INFO,
+} from 'common/timestamp_converter';
import {Analytics} from 'logging/analytics';
import {ProgressListener} from 'messaging/progress_listener';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
-import {
- CorruptedArchive,
- NoCommonTimestampType,
- NoInputFiles,
-} from 'messaging/user_warnings';
+import {CorruptedArchive, NoInputFiles} from 'messaging/user_warnings';
import {FileAndParsers} from 'parsers/file_and_parsers';
import {ParserFactory as LegacyParserFactory} from 'parsers/legacy/parser_factory';
import {TracesParserFactory} from 'parsers/legacy/traces_parser_factory';
@@ -48,7 +45,7 @@
private tracesParserFactory = new TracesParserFactory();
private traces = new Traces();
private downloadArchiveFilename?: string;
- private timestampFactory = NO_TIMEZONE_OFFSET_FACTORY;
+ private timestampConverter = new TimestampConverter(UTC_TIMEZONE_INFO);
async loadFiles(
files: File[],
@@ -83,24 +80,19 @@
this.traces = new Traces();
- const commonTimestampType = this.loadedParsers.findCommonTimestampType();
- if (commonTimestampType === undefined) {
- notificationListener.onNotifications([new NoCommonTimestampType()]);
- return;
- }
-
this.loadedParsers.getParsers().forEach((parser) => {
- const trace = Trace.fromParser(parser, commonTimestampType);
+ const trace = Trace.fromParser(parser);
this.traces.setTrace(parser.getTraceType(), trace);
Analytics.Tracing.logTraceLoaded(parser);
});
const tracesParsers = await this.tracesParserFactory.createParsers(
this.traces,
+ this.timestampConverter,
);
tracesParsers.forEach((tracesParser) => {
- const trace = Trace.fromParser(tracesParser, commonTimestampType);
+ const trace = Trace.fromParser(tracesParser);
this.traces.setTrace(trace.type, trace);
});
@@ -141,6 +133,16 @@
}
async buildTraces() {
+ for (const trace of this.traces) {
+ if (trace.lengthEntries === 0) {
+ continue;
+ }
+ const timestamp = trace.getEntry(0).getTimestamp();
+ if (timestamp.getValueNs() !== INVALID_TIME_NS) {
+ this.timestampConverter.initializeUTCOffset(timestamp);
+ break;
+ }
+ }
await new FrameMapper(this.traces).computeMapping();
}
@@ -152,8 +154,8 @@
return this.downloadArchiveFilename ?? 'winscope';
}
- getTimestampFactory(): TimestampFactory {
- return this.timestampFactory;
+ getTimestampConverter(): TimestampConverter {
+ return this.timestampConverter;
}
async getScreenRecordingVideo(): Promise<undefined | Blob> {
@@ -170,7 +172,7 @@
clear() {
this.loadedParsers.clear();
this.traces = new Traces();
- this.timestampFactory = NO_TIMEZONE_OFFSET_FACTORY;
+ this.timestampConverter = new TimestampConverter(UTC_TIMEZONE_INFO);
this.downloadArchiveFilename = undefined;
}
@@ -184,7 +186,9 @@
notificationListener,
);
if (filterResult.timezoneInfo) {
- this.timestampFactory = new TimestampFactory(filterResult.timezoneInfo);
+ this.timestampConverter = new TimestampConverter(
+ filterResult.timezoneInfo,
+ );
}
if (!filterResult.perfetto && filterResult.legacy.length === 0) {
@@ -194,7 +198,7 @@
const legacyParsers = await new LegacyParserFactory().createParsers(
filterResult.legacy,
- this.timestampFactory,
+ this.timestampConverter,
progressListener,
notificationListener,
);
@@ -204,13 +208,41 @@
if (filterResult.perfetto) {
const parsers = await new PerfettoParserFactory().createParsers(
filterResult.perfetto,
- this.timestampFactory,
+ this.timestampConverter,
progressListener,
notificationListener,
);
perfettoParsers = new FileAndParsers(filterResult.perfetto, parsers);
}
+ const monotonicTimeOffset =
+ this.loadedParsers.getLatestRealToMonotonicOffset(
+ legacyParsers
+ .map((fileAndParser) => fileAndParser.parser)
+ .concat(perfettoParsers?.parsers ?? []),
+ );
+
+ const realToBootTimeOffset =
+ this.loadedParsers.getLatestRealToBootTimeOffset(
+ legacyParsers
+ .map((fileAndParser) => fileAndParser.parser)
+ .concat(perfettoParsers?.parsers ?? []),
+ );
+
+ if (monotonicTimeOffset !== undefined) {
+ this.timestampConverter.setRealToMonotonicTimeOffsetNs(
+ monotonicTimeOffset,
+ );
+ }
+ if (realToBootTimeOffset !== undefined) {
+ this.timestampConverter.setRealToBootTimeOffsetNs(realToBootTimeOffset);
+ }
+
+ perfettoParsers?.parsers.forEach((p) => p.createTimestamps());
+ legacyParsers.forEach((fileAndParser) =>
+ fileAndParser.parser.createTimestamps(),
+ );
+
this.loadedParsers.addParsers(
legacyParsers,
perfettoParsers,
diff --git a/tools/winscope/src/app/trace_pipeline_test.ts b/tools/winscope/src/app/trace_pipeline_test.ts
index 4858ae8..1989296 100644
--- a/tools/winscope/src/app/trace_pipeline_test.ts
+++ b/tools/winscope/src/app/trace_pipeline_test.ts
@@ -16,7 +16,6 @@
import {assertDefined} from 'common/assert_utils';
import {FileUtils} from 'common/file_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {ProgressListenerStub} from 'messaging/progress_listener_stub';
import {UserWarning} from 'messaging/user_warning';
import {
@@ -26,6 +25,7 @@
TraceOverridden,
UnsupportedFileFormat,
} from 'messaging/user_warnings';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesUtils} from 'test/unit/traces_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {TraceType} from 'trace/trace_type';
@@ -152,40 +152,15 @@
expect(traces.getTrace(TraceType.INPUT_METHOD_CLIENTS)).toBeDefined();
});
- it('detects bugreports and extracts timezone info from dumpstate_board.txt', async () => {
- const bugreportFiles = [
- await UnitTestUtils.getFixtureFile(
- 'bugreports/main_entry.txt',
- 'main_entry.txt',
- ),
- await UnitTestUtils.getFixtureFile(
- 'bugreports/dumpstate_board.txt',
- 'dumpstate_board.txt',
- ),
- await UnitTestUtils.getFixtureFile(
- 'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
- 'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
- ),
- await UnitTestUtils.getFixtureFile(
- 'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
- 'FS/data/misc/wmtrace/surface_flinger.bp',
- ),
- ];
- const bugreportArchive = new File(
- [await FileUtils.createZipArchive(bugreportFiles)],
- 'bugreport.zip',
+ it('detects bugreports and extracts utc offset directly', async () => {
+ await testTimezoneOffsetExtraction(
+ 'bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
);
+ });
- await loadFiles([bugreportArchive]);
- await expectLoadResult(1, []);
-
- const timestampFactory = tracePipeline.getTimestampFactory();
- expect(timestampFactory).not.toEqual(NO_TIMEZONE_OFFSET_FACTORY);
-
- const expectedTimestamp =
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889102062832n);
- expect(timestampFactory.makeRealTimestamp(1659107089102062832n)).toEqual(
- expectedTimestamp,
+ it('detects bugreports and extracts timezone info, then calculates utc offset', async () => {
+ await testTimezoneOffsetExtraction(
+ 'bugreports/bugreport-codename_beta-no-time-offset-UPB2.230407.019-2023-05-30-14-33-48.txt',
);
});
@@ -410,4 +385,40 @@
expect(warnings).toEqual(expectedWarnings);
expect(tracePipeline.getTraces().getSize()).toEqual(numberOfTraces);
}
+
+ async function testTimezoneOffsetExtraction(codenameFileName: string) {
+ const bugreportFiles = [
+ await UnitTestUtils.getFixtureFile(
+ 'bugreports/main_entry.txt',
+ 'main_entry.txt',
+ ),
+ await UnitTestUtils.getFixtureFile(
+ codenameFileName,
+ 'bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt',
+ ),
+ await UnitTestUtils.getFixtureFile(
+ 'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
+ 'FS/data/misc/wmtrace/surface_flinger.bp',
+ ),
+ ];
+ const bugreportArchive = new File(
+ [await FileUtils.createZipArchive(bugreportFiles)],
+ 'bugreport.zip',
+ );
+
+ await loadFiles([bugreportArchive]);
+ await expectLoadResult(1, []);
+
+ const timestampConverter = tracePipeline.getTimestampConverter();
+ expect(timestampConverter);
+ expect(timestampConverter.getUTCOffset()).toEqual('UTC+05:30');
+
+ const expectedTimestamp =
+ TimestampConverterUtils.makeRealTimestampWithUTCOffset(
+ 1659107089102062832n,
+ );
+ expect(
+ timestampConverter.makeTimestampFromMonotonicNs(14500282843n),
+ ).toEqual(expectedTimestamp);
+ }
});
diff --git a/tools/winscope/src/common/time.ts b/tools/winscope/src/common/time.ts
index e41f345..5209660 100644
--- a/tools/winscope/src/common/time.ts
+++ b/tools/winscope/src/common/time.ts
@@ -20,81 +20,57 @@
constructor(readonly from: Timestamp, readonly to: Timestamp) {}
}
-export enum TimestampType {
- ELAPSED = 'ELAPSED',
- REAL = 'REAL',
-}
-
export interface TimezoneInfo {
timezone: string;
locale: string;
+ utcOffsetMs: number | undefined;
+}
+
+export interface TimestampFormatter {
+ format(timestamp: Timestamp, hideNs?: boolean): string;
}
export class Timestamp {
- private readonly type: TimestampType;
- private readonly valueNs: bigint;
- private readonly timezoneOffset: bigint;
+ private readonly utcValueNs: bigint;
+ private readonly formatter: TimestampFormatter;
- constructor(type: TimestampType, valueNs: bigint, timezoneOffset = 0n) {
- this.type = type;
- this.valueNs = valueNs;
- this.timezoneOffset = timezoneOffset;
- }
-
- getType(): TimestampType {
- return this.type;
+ constructor(valueNs: bigint, formatter: TimestampFormatter) {
+ this.utcValueNs = valueNs;
+ this.formatter = formatter;
}
getValueNs(): bigint {
- return this.valueNs;
- }
-
- toUTC(): Timestamp {
- return new Timestamp(this.type, this.valueNs - this.timezoneOffset);
+ return this.utcValueNs;
}
valueOf(): bigint {
- return this.getValueNs();
+ return this.utcValueNs;
}
in(range: TimeRange): boolean {
- if (range.from.type !== this.type || range.to.type !== this.type) {
- throw new Error('Mismatching timestamp types');
- }
-
return (
range.from.getValueNs() <= this.getValueNs() &&
this.getValueNs() <= range.to.getValueNs()
);
}
- add(nanoseconds: bigint): Timestamp {
- return new Timestamp(this.type, this.getValueNs() + nanoseconds);
+ add(n: bigint): Timestamp {
+ return new Timestamp(this.getValueNs() + n, this.formatter);
}
- plus(timestamp: Timestamp): Timestamp {
- this.validateTimestampArithmetic(timestamp);
- return new Timestamp(this.type, timestamp.getValueNs() + this.getValueNs());
- }
-
- minus(timestamp: Timestamp): Timestamp {
- this.validateTimestampArithmetic(timestamp);
- return new Timestamp(this.type, this.getValueNs() - timestamp.getValueNs());
+ minus(n: bigint): Timestamp {
+ return new Timestamp(this.getValueNs() - n, this.formatter);
}
times(n: bigint): Timestamp {
- return new Timestamp(this.type, this.getValueNs() * n);
+ return new Timestamp(this.getValueNs() * n, this.formatter);
}
div(n: bigint): Timestamp {
- return new Timestamp(this.type, this.getValueNs() / n);
+ return new Timestamp(this.getValueNs() / n, this.formatter);
}
- private validateTimestampArithmetic(timestamp: Timestamp) {
- if (timestamp.type !== this.type) {
- throw new Error(
- 'Attemping to do timestamp arithmetic on different timestamp types',
- );
- }
+ format(hideNs = false): string {
+ return this.formatter.format(this, hideNs);
}
}
diff --git a/tools/winscope/src/common/time_duration.ts b/tools/winscope/src/common/time_duration.ts
new file mode 100644
index 0000000..13339e3
--- /dev/null
+++ b/tools/winscope/src/common/time_duration.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TimestampUtils} from './timestamp_utils';
+
+export class TimeDuration {
+ constructor(private timeDiffNs: bigint) {}
+ getValueNs(): bigint {
+ return this.timeDiffNs;
+ }
+
+ format(hideNs = false): string {
+ return TimestampUtils.formatElapsedNs(this.timeDiffNs, hideNs);
+ }
+}
diff --git a/tools/winscope/src/common/time_test.ts b/tools/winscope/src/common/time_test.ts
new file mode 100644
index 0000000..b258721
--- /dev/null
+++ b/tools/winscope/src/common/time_test.ts
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {TIME_UNIT_TO_NANO} from './time_units';
+
+describe('Timestamp', () => {
+ describe('arithmetic', () => {
+ const REAL_TIMESTAMP_10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const REAL_TIMESTAMP_20 = TimestampConverterUtils.makeRealTimestamp(20n);
+ const ELAPSED_TIMESTAMP_10 =
+ TimestampConverterUtils.makeElapsedTimestamp(10n);
+ const ELAPSED_TIMESTAMP_20 =
+ TimestampConverterUtils.makeElapsedTimestamp(20n);
+
+ it('can add', () => {
+ let timestamp = REAL_TIMESTAMP_10.add(REAL_TIMESTAMP_20.getValueNs());
+ expect(timestamp.getValueNs()).toBe(30n);
+
+ timestamp = ELAPSED_TIMESTAMP_10.add(ELAPSED_TIMESTAMP_20.getValueNs());
+ expect(timestamp.getValueNs()).toBe(30n);
+ });
+
+ it('can subtract', () => {
+ let timestamp = REAL_TIMESTAMP_20.minus(REAL_TIMESTAMP_10.getValueNs());
+ expect(timestamp.getValueNs()).toBe(10n);
+
+ timestamp = ELAPSED_TIMESTAMP_20.minus(ELAPSED_TIMESTAMP_10.getValueNs());
+ expect(timestamp.getValueNs()).toBe(10n);
+ });
+
+ it('can divide', () => {
+ let timestamp = TimestampConverterUtils.makeRealTimestamp(10n).div(2n);
+ expect(timestamp.getValueNs()).toBe(5n);
+
+ timestamp = ELAPSED_TIMESTAMP_10.div(2n);
+ expect(timestamp.getValueNs()).toBe(5n);
+ });
+ });
+
+ describe('formatting', () => {
+ const MILLISECOND = BigInt(TIME_UNIT_TO_NANO.ms);
+ const SECOND = BigInt(TIME_UNIT_TO_NANO.s);
+ const MINUTE = BigInt(TIME_UNIT_TO_NANO.m);
+ const HOUR = BigInt(TIME_UNIT_TO_NANO.h);
+ const DAY = BigInt(TIME_UNIT_TO_NANO.d);
+
+ it('elapsed timestamps', () => {
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(0n).format(true),
+ ).toEqual('0ms');
+ expect(TimestampConverterUtils.makeElapsedTimestamp(0n).format()).toEqual(
+ '0ns',
+ );
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(1000n).format(true),
+ ).toEqual('0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(1000n).format(),
+ ).toEqual('1000ns');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(MILLISECOND - 1n).format(
+ true,
+ ),
+ ).toEqual('0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(MILLISECOND).format(true),
+ ).toEqual('1ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(10n * MILLISECOND).format(
+ true,
+ ),
+ ).toEqual('10ms');
+
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(SECOND - 1n).format(true),
+ ).toEqual('999ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(SECOND).format(true),
+ ).toEqual('1s0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ SECOND + MILLISECOND,
+ ).format(true),
+ ).toEqual('1s1ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ SECOND + MILLISECOND,
+ ).format(),
+ ).toEqual('1s1ms0ns');
+
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(MINUTE - 1n).format(true),
+ ).toEqual('59s999ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(MINUTE).format(true),
+ ).toEqual('1m0s0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ MINUTE + SECOND + MILLISECOND,
+ ).format(true),
+ ).toEqual('1m1s1ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ MINUTE + SECOND + MILLISECOND + 1n,
+ ).format(true),
+ ).toEqual('1m1s1ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ MINUTE + SECOND + MILLISECOND + 1n,
+ ).format(),
+ ).toEqual('1m1s1ms1ns');
+
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(HOUR - 1n).format(true),
+ ).toEqual('59m59s999ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(HOUR - 1n).format(),
+ ).toEqual('59m59s999ms999999ns');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(HOUR).format(true),
+ ).toEqual('1h0m0s0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ HOUR + MINUTE + SECOND + MILLISECOND,
+ ).format(true),
+ ).toEqual('1h1m1s1ms');
+
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(DAY - 1n).format(true),
+ ).toEqual('23h59m59s999ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(DAY).format(true),
+ ).toEqual('1d0h0m0s0ms');
+ expect(
+ TimestampConverterUtils.makeElapsedTimestamp(
+ DAY + HOUR + MINUTE + SECOND + MILLISECOND,
+ ).format(true),
+ ).toEqual('1d1h1m1s1ms');
+ });
+
+ it('real timestamps without timezone info', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(0n).format(true),
+ ).toEqual('1970-01-01T00:00:00.000');
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ ).format(true),
+ ).toEqual('2022-11-10T22:04:54.186');
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(NOV_10_2022).format(true),
+ ).toEqual('2022-11-10T00:00:00.000');
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(NOV_10_2022 + 1n).format(
+ true,
+ ),
+ ).toEqual('2022-11-10T00:00:00.000');
+
+ expect(TimestampConverterUtils.makeRealTimestamp(0n).format()).toEqual(
+ '1970-01-01T00:00:00.000000000',
+ );
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ ).format(),
+ ).toEqual('2022-11-10T22:04:54.186123212');
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(NOV_10_2022).format(),
+ ).toEqual('2022-11-10T00:00:00.000000000');
+ expect(
+ TimestampConverterUtils.makeRealTimestamp(NOV_10_2022 + 1n).format(),
+ ).toEqual('2022-11-10T00:00:00.000000001');
+ });
+
+ it('real timestamps with timezone info', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ 0n,
+ ).format(true),
+ ).toEqual('1970-01-01T05:30:00.000');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ ).format(true),
+ ).toEqual('2022-11-11T03:34:54.186');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022,
+ ).format(true),
+ ).toEqual('2022-11-10T05:30:00.000');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022 + 1n,
+ ).format(true),
+ ).toEqual('2022-11-10T05:30:00.000');
+
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ 0n,
+ ).format(),
+ ).toEqual('1970-01-01T05:30:00.000000000');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ ).format(),
+ ).toEqual('2022-11-11T03:34:54.186123212');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022,
+ ).format(),
+ ).toEqual('2022-11-10T05:30:00.000000000');
+ expect(
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ NOV_10_2022 + 1n,
+ ).format(),
+ ).toEqual('2022-11-10T05:30:00.000000001');
+ });
+ });
+});
diff --git a/tools/winscope/src/common/time_units.ts b/tools/winscope/src/common/time_units.ts
new file mode 100644
index 0000000..fbe25d7
--- /dev/null
+++ b/tools/winscope/src/common/time_units.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export enum TIME_UNIT_TO_NANO {
+ ns = 1,
+ ms = 1000000,
+ s = 1000000 * 1000,
+ m = 1000000 * 1000 * 60,
+ h = 1000000 * 1000 * 60 * 60,
+ d = 1000000 * 1000 * 60 * 60 * 24,
+}
+
+export const TIME_UNITS = [
+ {nanosInUnit: TIME_UNIT_TO_NANO['ns'], unit: 'ns'},
+ {nanosInUnit: TIME_UNIT_TO_NANO['ms'], unit: 'ms'},
+ {nanosInUnit: TIME_UNIT_TO_NANO['s'], unit: 's'},
+ {nanosInUnit: TIME_UNIT_TO_NANO['m'], unit: 'm'},
+ {nanosInUnit: TIME_UNIT_TO_NANO['h'], unit: 'h'},
+ {nanosInUnit: TIME_UNIT_TO_NANO['d'], unit: 'd'},
+];
diff --git a/tools/winscope/src/common/times_test.ts b/tools/winscope/src/common/times_test.ts
deleted file mode 100644
index b2520f2..0000000
--- a/tools/winscope/src/common/times_test.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {TimestampType} from './time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from './timestamp_factory';
-
-describe('Timestamp', () => {
- describe('arithmetic', () => {
- const REAL_TIMESTAMP_10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const REAL_TIMESTAMP_20 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(20n);
- const ELAPSED_TIMESTAMP_10 =
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n);
- const ELAPSED_TIMESTAMP_20 =
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(20n);
-
- it('can add', () => {
- let timestamp = REAL_TIMESTAMP_10.plus(REAL_TIMESTAMP_20);
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(30n);
-
- timestamp = ELAPSED_TIMESTAMP_10.plus(ELAPSED_TIMESTAMP_20);
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(30n);
- });
-
- it('can subtract', () => {
- let timestamp = REAL_TIMESTAMP_20.minus(REAL_TIMESTAMP_10);
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(10n);
-
- timestamp = ELAPSED_TIMESTAMP_20.minus(ELAPSED_TIMESTAMP_10);
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(10n);
- });
-
- it('can divide', () => {
- let timestamp = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n).div(2n);
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(5n);
-
- timestamp = ELAPSED_TIMESTAMP_10.div(2n);
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(5n);
- });
-
- it('fails between different timestamp types', () => {
- const error = new Error(
- 'Attemping to do timestamp arithmetic on different timestamp types',
- );
- expect(() => {
- REAL_TIMESTAMP_20.minus(ELAPSED_TIMESTAMP_10);
- }).toThrow(error);
- expect(() => {
- REAL_TIMESTAMP_20.plus(ELAPSED_TIMESTAMP_10);
- }).toThrow(error);
- expect(() => {
- ELAPSED_TIMESTAMP_20.minus(REAL_TIMESTAMP_10);
- }).toThrow(error);
- expect(() => {
- ELAPSED_TIMESTAMP_20.plus(REAL_TIMESTAMP_10);
- }).toThrow(error);
- });
- });
-});
diff --git a/tools/winscope/src/common/timestamp_converter.ts b/tools/winscope/src/common/timestamp_converter.ts
new file mode 100644
index 0000000..b5a40f5
--- /dev/null
+++ b/tools/winscope/src/common/timestamp_converter.ts
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {assertDefined, assertTrue} from './assert_utils';
+import {
+ INVALID_TIME_NS,
+ Timestamp,
+ TimestampFormatter,
+ TimezoneInfo,
+} from './time';
+import {TimestampUtils} from './timestamp_utils';
+import {TIME_UNITS, TIME_UNIT_TO_NANO} from './time_units';
+import {UTCOffset} from './utc_offset';
+
+// Pre-T traces do not provide real-to-boottime or real-to-monotonic offsets,so
+// we group their timestamps under the "ELAPSED" umbrella term, and hope that
+// the CPU was not suspended before the tracing session, causing them to diverge.
+enum TimestampType {
+ ELAPSED,
+ REAL,
+}
+
+class RealTimestampFormatter implements TimestampFormatter {
+ constructor(private utcOffset: UTCOffset) {}
+
+ setUTCOffset(value: UTCOffset) {
+ this.utcOffset = value;
+ }
+
+ format(timestamp: Timestamp, hideNs?: boolean | undefined): string {
+ const timestampNanos =
+ timestamp.getValueNs() + (this.utcOffset.getValueNs() ?? 0n);
+ const ms = timestampNanos / 1000000n;
+ const extraNanos = timestampNanos % 1000000n;
+ const formattedTimestamp = new Date(Number(ms))
+ .toISOString()
+ .replace('Z', '');
+
+ if (hideNs) {
+ return formattedTimestamp;
+ } else {
+ return `${formattedTimestamp}${extraNanos.toString().padStart(6, '0')}`;
+ }
+ }
+}
+const REAL_TIMESTAMP_FORMATTER_UTC = new RealTimestampFormatter(
+ new UTCOffset(),
+);
+
+class ElapsedTimestampFormatter {
+ format(timestamp: Timestamp, hideNs = false): string {
+ const timestampNanos = timestamp.getValueNs();
+ return TimestampUtils.formatElapsedNs(timestampNanos, hideNs);
+ }
+}
+const ELAPSED_TIMESTAMP_FORMATTER = new ElapsedTimestampFormatter();
+
+export interface ParserTimestampConverter {
+ makeTimestampFromRealNs(valueNs: bigint): Timestamp;
+ makeTimestampFromMonotonicNs(valueNs: bigint): Timestamp;
+ makeTimestampFromBootTimeNs(valueNs: bigint): Timestamp;
+ makeZeroTimestamp(): Timestamp;
+}
+
+export interface ComponentTimestampConverter {
+ makeTimestampFromHuman(timestampHuman: string): Timestamp;
+ getUTCOffset(): string;
+ makeTimestampFromNs(valueNs: bigint): Timestamp;
+ canMakeRealTimestamps(): boolean;
+}
+
+export interface RemoteToolTimestampConverter {
+ tryMakeTimestampForRemoteTool(timestamp: Timestamp): Timestamp | undefined;
+}
+
+export class TimestampConverter
+ implements
+ ParserTimestampConverter,
+ ComponentTimestampConverter,
+ RemoteToolTimestampConverter
+{
+ private readonly utcOffset = new UTCOffset();
+ private readonly realTimestampFormatter = new RealTimestampFormatter(
+ this.utcOffset,
+ );
+ private createdTimestampType: TimestampType | undefined;
+
+ constructor(
+ private timezoneInfo: TimezoneInfo,
+ private realToMonotonicTimeOffsetNs?: bigint,
+ private realToBootTimeOffsetNs?: bigint,
+ ) {
+ if (timezoneInfo.utcOffsetMs !== undefined) {
+ this.utcOffset.initialize(
+ BigInt(timezoneInfo.utcOffsetMs * TIME_UNIT_TO_NANO.ms),
+ );
+ }
+ }
+
+ initializeUTCOffset(timestamp: Timestamp) {
+ if (
+ this.utcOffset.getValueNs() !== undefined ||
+ !this.canMakeRealTimestamps()
+ ) {
+ return;
+ }
+ const utcValueNs = timestamp.getValueNs();
+ const localNs =
+ this.timezoneInfo.timezone !== 'UTC'
+ ? this.addTimezoneOffset(this.timezoneInfo.timezone, utcValueNs)
+ : utcValueNs;
+ const utcOffsetNs = localNs - utcValueNs;
+ this.utcOffset.initialize(utcOffsetNs);
+ console.warn(
+ 'Failed to initialized timezone offset due to invalid time difference.',
+ );
+ }
+
+ setRealToMonotonicTimeOffsetNs(ns: bigint) {
+ if (this.realToMonotonicTimeOffsetNs !== undefined) {
+ return;
+ }
+ this.realToMonotonicTimeOffsetNs = ns;
+ }
+
+ setRealToBootTimeOffsetNs(ns: bigint) {
+ if (this.realToBootTimeOffsetNs !== undefined) {
+ return;
+ }
+ this.realToBootTimeOffsetNs = ns;
+ }
+
+ getUTCOffset(): string {
+ return this.utcOffset.format();
+ }
+
+ makeTimestampFromMonotonicNs(valueNs: bigint): Timestamp {
+ if (this.realToMonotonicTimeOffsetNs !== undefined) {
+ return this.makeRealTimestamp(valueNs + this.realToMonotonicTimeOffsetNs);
+ }
+ return this.makeElapsedTimestamp(valueNs);
+ }
+
+ makeTimestampFromBootTimeNs(valueNs: bigint): Timestamp {
+ if (this.realToBootTimeOffsetNs !== undefined) {
+ return this.makeRealTimestamp(valueNs + this.realToBootTimeOffsetNs);
+ }
+ return this.makeElapsedTimestamp(valueNs);
+ }
+
+ makeTimestampFromRealNs(valueNs: bigint): Timestamp {
+ return this.makeRealTimestamp(valueNs);
+ }
+
+ tryMakeTimestampFromRealNs(valueNs: bigint): Timestamp | undefined {
+ if (!this.canMakeRealTimestamps()) {
+ return undefined;
+ }
+ return this.makeRealTimestamp(valueNs);
+ }
+
+ makeTimestampFromHuman(timestampHuman: string): Timestamp {
+ if (TimestampUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX.test(timestampHuman)) {
+ return this.makeTimestampfromHumanElapsed(timestampHuman);
+ }
+
+ if (TimestampUtils.HUMAN_REAL_TIMESTAMP_REGEX.test(timestampHuman)) {
+ return assertDefined(this.makeTimestampFromHumanReal(timestampHuman));
+ }
+
+ throw Error('Invalid timestamp format');
+ }
+
+ makeTimestampFromNs(valueNs: bigint): Timestamp {
+ return new Timestamp(
+ valueNs,
+ this.canMakeRealTimestamps()
+ ? this.realTimestampFormatter
+ : ELAPSED_TIMESTAMP_FORMATTER,
+ );
+ }
+
+ makeZeroTimestamp(): Timestamp {
+ if (this.canMakeRealTimestamps()) {
+ return new Timestamp(INVALID_TIME_NS, REAL_TIMESTAMP_FORMATTER_UTC);
+ } else {
+ return new Timestamp(INVALID_TIME_NS, ELAPSED_TIMESTAMP_FORMATTER);
+ }
+ }
+
+ tryMakeTimestampForRemoteTool(timestamp: Timestamp): Timestamp | undefined {
+ if (this.canMakeRealTimestamps()) {
+ return timestamp;
+ }
+ return undefined;
+ }
+
+ canMakeRealTimestamps(): boolean {
+ return this.createdTimestampType === TimestampType.REAL;
+ }
+
+ private makeRealTimestamp(valueNs: bigint): Timestamp {
+ assertTrue(
+ this.createdTimestampType === undefined ||
+ this.createdTimestampType === TimestampType.REAL,
+ );
+ this.createdTimestampType = TimestampType.REAL;
+ return new Timestamp(valueNs, this.realTimestampFormatter);
+ }
+
+ private makeElapsedTimestamp(valueNs: bigint): Timestamp {
+ assertTrue(
+ this.createdTimestampType === undefined ||
+ this.createdTimestampType === TimestampType.ELAPSED,
+ );
+ this.createdTimestampType = TimestampType.ELAPSED;
+ return new Timestamp(valueNs, ELAPSED_TIMESTAMP_FORMATTER);
+ }
+
+ private makeTimestampFromHumanReal(timestampHuman: string): Timestamp {
+ // Remove trailing Z if present
+ timestampHuman = timestampHuman.replace('Z', '');
+
+ // Date.parse only considers up to millisecond precision,
+ // so only pass in YYYY-MM-DDThh:mm:ss
+ let nanos = 0n;
+ if (timestampHuman.includes('.')) {
+ const [datetime, ns] = timestampHuman.split('.');
+ nanos += BigInt(Math.floor(Number(ns.padEnd(9, '0'))));
+ timestampHuman = datetime;
+ }
+
+ timestampHuman += this.utcOffset.format().slice(3);
+
+ return this.makeTimestampFromRealNs(
+ BigInt(Date.parse(timestampHuman)) * BigInt(TIME_UNIT_TO_NANO['ms']) +
+ BigInt(nanos),
+ );
+ }
+
+ private makeTimestampfromHumanElapsed(timestampHuman: string): Timestamp {
+ const usedUnits = timestampHuman.split(/[0-9]+/).filter((it) => it !== '');
+ const usedValues = timestampHuman
+ .split(/[a-z]+/)
+ .filter((it) => it !== '')
+ .map((it) => Math.floor(Number(it)));
+
+ let ns = BigInt(0);
+
+ for (let i = 0; i < usedUnits.length; i++) {
+ const unit = usedUnits[i];
+ const value = usedValues[i];
+ const unitData = TIME_UNITS.find((it) => it.unit === unit)!;
+ ns += BigInt(unitData.nanosInUnit) * BigInt(value);
+ }
+
+ return this.makeElapsedTimestamp(ns);
+ }
+
+ private addTimezoneOffset(timezone: string, timestampNs: bigint): bigint {
+ const utcDate = new Date(Number(timestampNs / 1000000n));
+ const timezoneDateFormatted = utcDate.toLocaleString('en-US', {
+ timeZone: timezone,
+ });
+ const timezoneDate = new Date(timezoneDateFormatted);
+
+ let daysDiff = timezoneDate.getDay() - utcDate.getDay(); // day of the week
+ if (daysDiff > 1) {
+ // Saturday in timezone, Sunday in UTC
+ daysDiff = -1;
+ } else if (daysDiff < -1) {
+ // Sunday in timezone, Saturday in UTC
+ daysDiff = 1;
+ }
+
+ const hoursDiff =
+ timezoneDate.getHours() - utcDate.getHours() + daysDiff * 24;
+ const minutesDiff = timezoneDate.getMinutes() - utcDate.getMinutes();
+ const localTimezoneOffsetMinutes = utcDate.getTimezoneOffset();
+
+ return (
+ timestampNs +
+ BigInt(hoursDiff * 3.6e12) +
+ BigInt(minutesDiff * 6e10) -
+ BigInt(localTimezoneOffsetMinutes * 6e10)
+ );
+ }
+}
+
+export const UTC_TIMEZONE_INFO = {
+ timezone: 'UTC',
+ locale: 'en-US',
+ utcOffsetMs: 0,
+};
diff --git a/tools/winscope/src/common/timestamp_converter_test.ts b/tools/winscope/src/common/timestamp_converter_test.ts
new file mode 100644
index 0000000..b4c607b
--- /dev/null
+++ b/tools/winscope/src/common/timestamp_converter_test.ts
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {UnitTestUtils} from 'test/unit/utils';
+import {TimestampConverter} from './timestamp_converter';
+import {TIME_UNIT_TO_NANO} from './time_units';
+
+describe('TimestampConverter', () => {
+ const testElapsedNs = 100n;
+ const testRealNs = 1659243341051481088n; // Sun, 31 Jul 2022 04:55:41 GMT to test timestamp conversion between different days
+ const testMonotonicTimeOffsetNs = 500n;
+ const testRealToBootTimeOffsetNs = 1000n;
+
+ const MILLISECOND = BigInt(TIME_UNIT_TO_NANO.ms);
+ const SECOND = BigInt(TIME_UNIT_TO_NANO.s);
+ const MINUTE = BigInt(TIME_UNIT_TO_NANO.m);
+ const HOUR = BigInt(TIME_UNIT_TO_NANO.h);
+ const DAY = BigInt(TIME_UNIT_TO_NANO.d);
+
+ beforeAll(() => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
+ });
+
+ describe('makes timestamps from ns without timezone info', () => {
+ const converterWithMonotonicOffset = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ );
+ converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
+ testMonotonicTimeOffsetNs,
+ );
+
+ const converterWithBootTimeOffset = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ );
+ converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
+ testRealToBootTimeOffsetNs,
+ );
+
+ it('can create real-formatted timestamp without offset set', () => {
+ const timestamp = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ ).makeTimestampFromRealNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(testRealNs);
+ expect(timestamp.format()).toEqual('2022-07-31T04:55:41.051481088');
+ });
+
+ it('can create real-formatted timestamp with real to monotonic offset', () => {
+ const timestamp =
+ converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(
+ testRealNs + testMonotonicTimeOffsetNs,
+ );
+ expect(timestamp.format()).toEqual('2022-07-31T04:55:41.051481588');
+ });
+
+ it('can create real-formatted timestamp with real to boot time offset', () => {
+ const timestamp =
+ converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(
+ testRealNs + testRealToBootTimeOffsetNs,
+ );
+ expect(timestamp.format()).toEqual('2022-07-31T04:55:41.051482088');
+ });
+
+ it('can create elapsed-formatted timestamp', () => {
+ const timestamp = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ ).makeTimestampFromMonotonicNs(testElapsedNs);
+ expect(timestamp.getValueNs()).toBe(testElapsedNs);
+ expect(timestamp.format()).toEqual('100ns');
+ });
+
+ it('formats real-formatted timestamp with offset correctly', () => {
+ expect(
+ converterWithMonotonicOffset
+ .makeTimestampFromMonotonicNs(100n)
+ .format(),
+ ).toEqual('1970-01-01T00:00:00.000000600');
+ expect(
+ converterWithMonotonicOffset
+ .makeTimestampFromRealNs(100n * MILLISECOND)
+ .format(true),
+ ).toEqual('1970-01-01T00:00:00.100');
+ });
+ });
+
+ describe('makes timestamps from ns with timezone info', () => {
+ const converterWithMonotonicOffset = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ );
+ converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
+ testMonotonicTimeOffsetNs,
+ );
+
+ const converterWithBootTimeOffset = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ );
+ converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
+ testRealToBootTimeOffsetNs,
+ );
+
+ it('can create real-formatted timestamp without offset set', () => {
+ const timestamp = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ ).makeTimestampFromRealNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(testRealNs);
+ expect(timestamp.format()).toEqual('2022-07-31T10:25:41.051481088');
+ });
+
+ it('can create real-formatted timestamp with monotonic offset', () => {
+ const timestamp =
+ converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(
+ testRealNs + testMonotonicTimeOffsetNs,
+ );
+ expect(timestamp.format()).toEqual('2022-07-31T10:25:41.051481588');
+ });
+
+ it('can create real-formatted timestamp with real to boot time offset', () => {
+ const timestamp =
+ converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
+ expect(timestamp.getValueNs()).toBe(
+ testRealNs + testRealToBootTimeOffsetNs,
+ );
+ expect(timestamp.format()).toEqual('2022-07-31T10:25:41.051482088');
+ });
+
+ it('can create elapsed-formatted timestamp', () => {
+ const timestamp = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ ).makeTimestampFromMonotonicNs(testElapsedNs);
+ expect(timestamp.getValueNs()).toBe(testElapsedNs);
+ expect(timestamp.format()).toEqual('100ns');
+ });
+
+ describe('adds correct offset for different timezones', () => {
+ it('creates correct real-formatted timestamps for different timezones', () => {
+ expect(
+ new TimestampConverter(
+ {
+ timezone: 'Europe/London',
+ locale: 'en-US',
+ utcOffsetMs: 3600000,
+ },
+ 0n,
+ )
+ .makeTimestampFromRealNs(testRealNs)
+ .format(),
+ ).toEqual('2022-07-31T05:55:41.051481088');
+ expect(
+ new TimestampConverter(
+ {
+ timezone: 'Europe/Zurich',
+ locale: 'en-US',
+ utcOffsetMs: 7200000,
+ },
+ 0n,
+ )
+ .makeTimestampFromRealNs(testRealNs)
+ .format(),
+ ).toEqual('2022-07-31T06:55:41.051481088');
+ expect(
+ new TimestampConverter(
+ {
+ timezone: 'America/Los_Angeles',
+ locale: 'en-US',
+ utcOffsetMs: -25200000,
+ },
+ 0n,
+ )
+ .makeTimestampFromRealNs(testRealNs)
+ .format(),
+ ).toEqual('2022-07-30T21:55:41.051481088');
+ expect(
+ new TimestampConverter(
+ {
+ timezone: 'Asia/Kolkata',
+ locale: 'en-US',
+ utcOffsetMs: 19800000,
+ },
+ 0n,
+ )
+ .makeTimestampFromRealNs(testRealNs)
+ .format(),
+ ).toEqual('2022-07-31T10:25:41.051481088');
+ });
+ });
+ });
+
+ describe('makes timestamps from string without timezone info', () => {
+ const converterWithoutOffsets = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ );
+
+ const converterWithMonotonicOffset = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ );
+ converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
+ testMonotonicTimeOffsetNs,
+ );
+
+ it('makeTimestampfromHumanElapsed', () => {
+ expect(converterWithoutOffsets.makeTimestampFromHuman('0ns')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1000ns')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(1000n),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('0ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(MILLISECOND),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('10ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(10n * MILLISECOND),
+ );
+
+ expect(converterWithoutOffsets.makeTimestampFromHuman('999ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(
+ 999n * MILLISECOND,
+ ),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1s')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1s0ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
+ );
+ expect(
+ converterWithoutOffsets.makeTimestampFromHuman('1s0ms0ns'),
+ ).toEqual(converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND));
+ expect(
+ converterWithoutOffsets.makeTimestampFromHuman('1s0ms1ns'),
+ ).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND + 1n),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('0d1s1ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(
+ SECOND + MILLISECOND,
+ ),
+ );
+
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1m0s0ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(MINUTE),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1m1s1ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(
+ MINUTE + SECOND + MILLISECOND,
+ ),
+ );
+
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1h0m')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(HOUR),
+ );
+ expect(
+ converterWithoutOffsets.makeTimestampFromHuman('1h1m1s1ms'),
+ ).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(
+ HOUR + MINUTE + SECOND + MILLISECOND,
+ ),
+ );
+
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1d0s1ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
+ );
+ expect(
+ converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s1ms'),
+ ).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(
+ DAY + HOUR + MINUTE + SECOND + MILLISECOND,
+ ),
+ );
+
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1d')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY),
+ );
+ expect(converterWithoutOffsets.makeTimestampFromHuman('1d1ms')).toEqual(
+ converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
+ );
+ });
+
+ it('makeTimestampfromHumanElapsed throws on invalid input format', () => {
+ const invalidFormatError = new Error('Invalid timestamp format');
+ expect(() =>
+ converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s0ns1ms'),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithoutOffsets.makeTimestampFromHuman('1dns'),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithoutOffsets.makeTimestampFromHuman('100'),
+ ).toThrow(invalidFormatError);
+ expect(() => converterWithoutOffsets.makeTimestampFromHuman('')).toThrow(
+ invalidFormatError,
+ );
+ });
+
+ it('makeTimestampFromHumanReal', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T22:04:54.186123212',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186123212n,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T22:04:54.186123212Z',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T22:04:54.186000212',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 212n,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T22:04:54.006000002',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.006000002',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.0',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.0100',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 +
+ 6n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 10n * MILLISECOND,
+ ),
+ );
+ expect(
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.0175328',
+ ),
+ ).toEqual(
+ converterWithMonotonicOffset.makeTimestampFromRealNs(
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
+ ),
+ );
+ });
+
+ it('makeTimestampFromHumanReal throws on invalid input format', () => {
+ const invalidFormatError = new Error('Invalid timestamp format');
+ expect(() =>
+ converterWithMonotonicOffset.makeTimestampFromHuman('100'),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '06h4m54s, 10 Nov 2022',
+ ),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithMonotonicOffset.makeTimestampFromHuman(''),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.',
+ ),
+ ).toThrow(invalidFormatError);
+ expect(() =>
+ converterWithMonotonicOffset.makeTimestampFromHuman(
+ '2022-11-10T06:04:54.1234567890',
+ ),
+ ).toThrow(invalidFormatError);
+ });
+
+ it('can reverse-date format', () => {
+ expect(
+ converterWithMonotonicOffset
+ .makeTimestampFromHuman('2022-11-10T22:04:54.186123212')
+ .format(),
+ ).toEqual('2022-11-10T22:04:54.186123212');
+ });
+ });
+
+ describe('makes timestamps from string with timezone info', () => {
+ const converter = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ );
+ converter.setRealToMonotonicTimeOffsetNs(testMonotonicTimeOffsetNs);
+
+ it('makeTimestampFromHumanReal', () => {
+ const NOV_10_2022 = 1668038400000n * MILLISECOND;
+ testMakeTimestampFromHumanReal(
+ '2022-11-11T03:34:54.186123212',
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ '2022-11-11T03:34:54.186123212',
+ );
+
+ testMakeTimestampFromHumanReal(
+ '2022-11-11T03:34:54.186123212Z',
+ NOV_10_2022 +
+ 22n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 186n * MILLISECOND +
+ 123212n,
+ '2022-11-11T03:34:54.186123212',
+ );
+
+ testMakeTimestampFromHumanReal(
+ '2022-11-10T11:34:54',
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
+ '2022-11-10T11:34:54.000000000',
+ );
+
+ testMakeTimestampFromHumanReal(
+ '2022-11-10T11:34:54.0',
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
+ '2022-11-10T11:34:54.000000000',
+ );
+
+ testMakeTimestampFromHumanReal(
+ '2022-11-10T11:34:54.0100',
+ NOV_10_2022 +
+ 6n * HOUR +
+ 4n * MINUTE +
+ 54n * SECOND +
+ 10n * MILLISECOND,
+ '2022-11-10T11:34:54.010000000',
+ );
+
+ testMakeTimestampFromHumanReal(
+ '2022-11-10T11:34:54.0175328',
+ NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
+ '2022-11-10T11:34:54.017532800',
+ );
+ });
+
+ it('can reverse-date format', () => {
+ expect(
+ converter
+ .makeTimestampFromHuman('2022-11-11T03:34:54.186123212')
+ .format(),
+ ).toEqual('2022-11-11T03:34:54.186123212');
+ });
+
+ function testMakeTimestampFromHumanReal(
+ timestampHuman: string,
+ expectedNs: bigint,
+ expectedFormattedTimestamp: string,
+ ) {
+ const timestamp = converter.makeTimestampFromHuman(timestampHuman);
+ expect(timestamp.getValueNs()).toEqual(expectedNs);
+ expect(timestamp.format()).toEqual(expectedFormattedTimestamp);
+ }
+ });
+});
diff --git a/tools/winscope/src/common/timestamp_factory.ts b/tools/winscope/src/common/timestamp_factory.ts
deleted file mode 100644
index c267502..0000000
--- a/tools/winscope/src/common/timestamp_factory.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {Timestamp, TimestampType, TimezoneInfo} from './time';
-
-export class TimestampFactory {
- constructor(
- public timezoneInfo: TimezoneInfo = {timezone: 'UTC', locale: 'en-US'},
- ) {}
-
- makeRealTimestamp(
- valueNs: bigint,
- realToElapsedTimeOffsetNs?: bigint,
- ): Timestamp {
- const valueWithRealtimeOffset = valueNs + (realToElapsedTimeOffsetNs ?? 0n);
- const localNs =
- this.timezoneInfo.timezone !== 'UTC'
- ? this.addTimezoneOffset(
- this.timezoneInfo.timezone,
- valueWithRealtimeOffset,
- )
- : valueWithRealtimeOffset;
- return new Timestamp(
- TimestampType.REAL,
- localNs,
- localNs - valueWithRealtimeOffset,
- );
- }
-
- makeElapsedTimestamp(valueNs: bigint): Timestamp {
- return new Timestamp(TimestampType.ELAPSED, valueNs);
- }
-
- canMakeTimestampFromType(
- type: TimestampType,
- realToElapsedTimeOffsetNs: bigint | undefined,
- ) {
- return (
- type === TimestampType.ELAPSED ||
- (type === TimestampType.REAL && realToElapsedTimeOffsetNs !== undefined)
- );
- }
-
- makeTimestampFromType(
- type: TimestampType,
- valueNs: bigint,
- realToElapsedTimeOffsetNs?: bigint,
- ): Timestamp {
- switch (type) {
- case TimestampType.REAL:
- if (realToElapsedTimeOffsetNs === undefined) {
- throw new Error(
- "realToElapsedTimeOffsetNs can't be undefined to use real timestamp",
- );
- }
- return this.makeRealTimestamp(valueNs, realToElapsedTimeOffsetNs);
- case TimestampType.ELAPSED:
- return this.makeElapsedTimestamp(valueNs);
- default:
- throw new Error('Unhandled timestamp type');
- }
- }
-
- private addTimezoneOffset(timezone: string, timestampNs: bigint): bigint {
- const utcDate = new Date(Number(timestampNs / 1000000n));
- const timezoneDateFormatted = utcDate.toLocaleString('en-US', {
- timeZone: timezone,
- });
- const timezoneDate = new Date(timezoneDateFormatted);
-
- let daysDiff = timezoneDate.getDay() - utcDate.getDay(); // day of the week
- if (daysDiff > 1) {
- // Saturday in timezone, Sunday in UTC
- daysDiff = -1;
- } else if (daysDiff < -1) {
- // Sunday in timezone, Saturday in UTC
- daysDiff = 1;
- }
-
- const hoursDiff =
- timezoneDate.getHours() - utcDate.getHours() + daysDiff * 24;
- const minutesDiff = timezoneDate.getMinutes() - utcDate.getMinutes();
- const localTimezoneOffsetMinutes = utcDate.getTimezoneOffset();
-
- return (
- timestampNs +
- BigInt(hoursDiff * 3.6e12) +
- BigInt(minutesDiff * 6e10) -
- BigInt(localTimezoneOffsetMinutes * 6e10)
- );
- }
-}
-
-export const NO_TIMEZONE_OFFSET_FACTORY = new TimestampFactory();
diff --git a/tools/winscope/src/common/timestamp_factory_test.ts b/tools/winscope/src/common/timestamp_factory_test.ts
deleted file mode 100644
index 8b2850c..0000000
--- a/tools/winscope/src/common/timestamp_factory_test.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {TimestampType} from './time';
-import {TimestampFactory} from './timestamp_factory';
-
-describe('TimestampFactory', () => {
- const testElapsedNs = 100n;
- const testRealNs = 1659243341051481088n; // Sun, 31 Jul 2022 04:55:41 GMT to test timestamp conversion between different days
- const testRealToElapsedOffsetNs = 500n;
-
- describe('without timezone info', () => {
- const factory = new TimestampFactory();
- it('can create real timestamp', () => {
- const timestamp = factory.makeRealTimestamp(testRealNs);
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(testRealNs);
- });
-
- it('can create real timestamp with offset', () => {
- const timestamp = factory.makeRealTimestamp(
- testRealNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs,
- );
- });
-
- it('can create elapsed timestamp', () => {
- const timestamp = factory.makeElapsedTimestamp(testElapsedNs);
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- });
-
- it('can create real timestamp from type', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.REAL,
- testRealNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs,
- );
- });
-
- it('can create elapsed timestamp from type', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.ELAPSED,
- testElapsedNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- });
-
- it('can create elapsed timestamp from type ignoring offset', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.ELAPSED,
- testElapsedNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- });
-
- it('throws error if creating real timestamp from type without offset', () => {
- expect(() =>
- factory.makeTimestampFromType(TimestampType.REAL, testRealNs),
- ).toThrow();
- });
- });
-
- describe('with timezone info', () => {
- const factory = new TimestampFactory({
- timezone: 'Asia/Kolkata',
- locale: 'en-US',
- });
- const expectedUtcOffsetNs = 19800000000000n;
-
- it('can create real timestamp', () => {
- const timestamp = factory.makeRealTimestamp(testRealNs);
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(testRealNs + expectedUtcOffsetNs);
- expect(timestamp.toUTC().getValueNs()).toBe(testRealNs);
- });
-
- it('can create real timestamp with offset', () => {
- const timestamp = factory.makeRealTimestamp(
- testRealNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs + expectedUtcOffsetNs,
- );
- expect(timestamp.toUTC().getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs,
- );
- });
-
- it('can create elapsed timestamp', () => {
- const timestamp = factory.makeElapsedTimestamp(testElapsedNs);
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- expect(timestamp.toUTC().getValueNs()).toBe(testElapsedNs);
- });
-
- it('can create real timestamp from type', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.REAL,
- testRealNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.REAL);
- expect(timestamp.getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs + expectedUtcOffsetNs,
- );
- expect(timestamp.toUTC().getValueNs()).toBe(
- testRealNs + testRealToElapsedOffsetNs,
- );
- });
-
- it('can create elapsed timestamp from type', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.ELAPSED,
- testElapsedNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- expect(timestamp.toUTC().getValueNs()).toBe(testElapsedNs);
- });
-
- it('can create elapsed timestamp from type ignoring offset', () => {
- const timestamp = factory.makeTimestampFromType(
- TimestampType.ELAPSED,
- testElapsedNs,
- testRealToElapsedOffsetNs,
- );
- expect(timestamp.getType()).toBe(TimestampType.ELAPSED);
- expect(timestamp.getValueNs()).toBe(testElapsedNs);
- expect(timestamp.toUTC().getValueNs()).toBe(testElapsedNs);
- });
-
- it('throws error if creating real timestamp from type without offset', () => {
- expect(() =>
- factory.makeTimestampFromType(TimestampType.REAL, testRealNs),
- ).toThrow();
- });
- });
-
- describe('adds correct offset for different timezones', () => {
- it('creates correct real timestamps for different timezones', () => {
- expect(
- new TimestampFactory({timezone: 'Europe/London', locale: 'en-US'})
- .makeRealTimestamp(testRealNs)
- .getValueNs(),
- ).toEqual(testRealNs + BigInt(1 * 3.6e12));
- expect(
- new TimestampFactory({timezone: 'Europe/Zurich', locale: 'en-US'})
- .makeRealTimestamp(testRealNs)
- .getValueNs(),
- ).toEqual(testRealNs + BigInt(2 * 3.6e12));
- expect(
- new TimestampFactory({timezone: 'America/Los_Angeles', locale: 'en-US'})
- .makeRealTimestamp(testRealNs)
- .getValueNs(),
- ).toEqual(testRealNs - BigInt(7 * 3.6e12));
- expect(
- new TimestampFactory({timezone: 'Asia/Kolkata', locale: 'en-US'})
- .makeRealTimestamp(testRealNs)
- .getValueNs(),
- ).toEqual(testRealNs + BigInt(5.5 * 3.6e12));
- });
-
- it('throws error for invalid timezone', () => {
- expect(() =>
- new TimestampFactory({
- timezone: 'Invalid/Timezone',
- locale: 'en-US',
- }).makeRealTimestamp(testRealNs),
- ).toThrow();
- });
- });
-});
diff --git a/tools/winscope/src/common/timestamp_utils.ts b/tools/winscope/src/common/timestamp_utils.ts
index c4f19ae..f41d16e 100644
--- a/tools/winscope/src/common/timestamp_utils.ts
+++ b/tools/winscope/src/common/timestamp_utils.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from './timestamp_factory';
+import {Timestamp} from 'common/time';
+import {TIME_UNITS} from './time_units';
export class TimestampUtils {
// (?=.) checks there is at least one character with a lookahead match
@@ -25,106 +25,11 @@
/^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])(\.[0-9]{1,9})?Z?$/;
static readonly NS_TIMESTAMP_REGEX = /^\s*[0-9]+(\s?ns)?\s*$/;
- 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: TimestampUtils.TO_NANO['ns'], unit: 'ns'},
- {nanosInUnit: TimestampUtils.TO_NANO['ms'], unit: 'ms'},
- {nanosInUnit: TimestampUtils.TO_NANO['s'], unit: 's'},
- {nanosInUnit: TimestampUtils.TO_NANO['m'], unit: 'm'},
- {nanosInUnit: TimestampUtils.TO_NANO['h'], unit: 'h'},
- {nanosInUnit: TimestampUtils.TO_NANO['d'], unit: 'd'},
- ];
-
static compareFn(a: Timestamp, b: Timestamp): number {
- if (a.getType() !== b.getType()) {
- throw new Error(
- 'Attempted to compare two timestamps with different type',
- );
- }
return Number(a.getValueNs() - b.getValueNs());
}
- static format(timestamp: Timestamp, hideNs = false): string {
- switch (timestamp.getType()) {
- case TimestampType.ELAPSED: {
- return TimestampUtils.nanosecondsToHumanElapsed(
- timestamp.getValueNs(),
- hideNs,
- );
- }
- case TimestampType.REAL: {
- return TimestampUtils.nanosecondsToHumanReal(
- timestamp.getValueNs(),
- hideNs,
- );
- }
- default: {
- throw Error('Unhandled timestamp type');
- }
- }
- }
-
- static parseHumanElapsed(timestampHuman: string): Timestamp {
- if (!TimestampUtils.HUMAN_ELAPSED_TIMESTAMP_REGEX.test(timestampHuman)) {
- throw Error('Invalid elapsed timestamp format');
- }
-
- const units = TimestampUtils.units;
-
- const usedUnits = timestampHuman.split(/[0-9]+/).filter((it) => it !== '');
- const usedValues = timestampHuman
- .split(/[a-z]+/)
- .filter((it) => it !== '')
- .map((it) => Math.floor(Number(it)));
-
- let ns = BigInt(0);
-
- for (let i = 0; i < usedUnits.length; i++) {
- const unit = usedUnits[i];
- const value = usedValues[i];
- const unitData = units.find((it) => it.unit === unit)!;
- ns += BigInt(unitData.nanosInUnit) * BigInt(value);
- }
-
- return NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(ns);
- }
-
- static parseHumanReal(timestampHuman: string): Timestamp {
- if (!TimestampUtils.HUMAN_REAL_TIMESTAMP_REGEX.test(timestampHuman)) {
- throw Error('Invalid real timestamp format');
- }
-
- // Add trailing Z if it isn't there yet
- if (timestampHuman[timestampHuman.length - 1] !== 'Z') {
- timestampHuman += 'Z';
- }
-
- // Date.parse only considers up to millisecond precision
- let nanoSeconds = 0;
- if (timestampHuman.includes('.')) {
- const milliseconds = timestampHuman.split('.')[1].replace('Z', '');
- nanoSeconds = Math.floor(Number(milliseconds.padEnd(9, '0').slice(3)));
- }
-
- return NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
- BigInt(Date.parse(timestampHuman)) *
- BigInt(TimestampUtils.TO_NANO['ms']) +
- BigInt(nanoSeconds),
- );
- }
-
static min(ts1: Timestamp, ts2: Timestamp): Timestamp {
- if (ts1.getType() !== ts2.getType()) {
- throw new Error("Can't compare timestamps of different types");
- }
if (ts2.getValueNs() < ts1.getValueNs()) {
return ts2;
}
@@ -133,9 +38,6 @@
}
static max(ts1: Timestamp, ts2: Timestamp): Timestamp {
- if (ts1.getType() !== ts2.getType()) {
- throw new Error("Can't compare timestamps of different types");
- }
if (ts2.getValueNs() > ts1.getValueNs()) {
return ts2;
}
@@ -143,16 +45,9 @@
return ts1;
}
- private static nanosecondsToHumanElapsed(
- timestampNanos: number | bigint,
- hideNs = true,
- ): string {
- timestampNanos = BigInt(timestampNanos);
- const units = TimestampUtils.units;
-
+ static formatElapsedNs(timestampNanos: bigint, hideNs = false): string {
let leftNanos = timestampNanos;
- const parts: Array<{value: bigint; unit: string}> = units
- .slice()
+ const parts: Array<{value: bigint; unit: string}> = TIME_UNITS.slice()
.reverse()
.map(({nanosInUnit, unit}) => {
let amountOfUnit = BigInt(0);
@@ -174,22 +69,4 @@
return parts.map((part) => `${part.value}${part.unit}`).join('');
}
-
- private static nanosecondsToHumanReal(
- timestampNanos: number | bigint,
- hideNs = true,
- ): string {
- timestampNanos = BigInt(timestampNanos);
- const ms = timestampNanos / 1000000n;
- const extraNanos = timestampNanos % 1000000n;
- const formattedTimestamp = new Date(Number(ms))
- .toISOString()
- .replace('Z', '');
-
- if (hideNs) {
- return formattedTimestamp;
- } else {
- return `${formattedTimestamp}${extraNanos.toString().padStart(6, '0')}`;
- }
- }
}
diff --git a/tools/winscope/src/common/timestamp_utils_test.ts b/tools/winscope/src/common/timestamp_utils_test.ts
index c7659ab..ae4178e 100644
--- a/tools/winscope/src/common/timestamp_utils_test.ts
+++ b/tools/winscope/src/common/timestamp_utils_test.ts
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from './timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {UnitTestUtils} from 'test/unit/utils';
import {TimestampUtils} from './timestamp_utils';
describe('TimestampUtils', () => {
@@ -22,436 +23,33 @@
const SECOND = BigInt(1000) * MILLISECOND;
const MINUTE = BigInt(60) * SECOND;
const HOUR = BigInt(60) * MINUTE;
- const DAY = BigInt(24) * HOUR;
+
+ beforeAll(() => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
+ });
describe('compareFn', () => {
- it('throws if timestamps have different type', () => {
- const real = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const elapsed = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n);
-
- expect(() => {
- TimestampUtils.compareFn(real, elapsed);
- }).toThrow();
- });
-
it('allows to sort arrays', () => {
const array = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n),
+ TimestampConverterUtils.makeRealTimestamp(100n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(12n),
+ TimestampConverterUtils.makeRealTimestamp(110n),
+ TimestampConverterUtils.makeRealTimestamp(11n),
];
array.sort(TimestampUtils.compareFn);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(110n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(11n),
+ TimestampConverterUtils.makeRealTimestamp(12n),
+ TimestampConverterUtils.makeRealTimestamp(100n),
+ TimestampConverterUtils.makeRealTimestamp(110n),
];
expect(array).toEqual(expected);
});
});
- it('nanosecondsToHuman', () => {
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- true,
- ),
- ).toEqual('0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- false,
- ),
- ).toEqual('0ns');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1000n),
- true,
- ),
- ).toEqual('0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1000n),
- false,
- ),
- ).toEqual('1000ns');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MILLISECOND - 1n),
- true,
- ),
- ).toEqual('0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MILLISECOND),
- true,
- ),
- ).toEqual('1ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n * MILLISECOND),
- true,
- ),
- ).toEqual('10ms');
-
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND - 1n),
- true,
- ),
- ).toEqual('999ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND),
- true,
- ),
- ).toEqual('1s0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND + MILLISECOND),
- true,
- ),
- ).toEqual('1s1ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND + MILLISECOND),
- false,
- ),
- ).toEqual('1s1ms0ns');
-
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MINUTE - 1n),
- true,
- ),
- ).toEqual('59s999ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MINUTE),
- true,
- ),
- ).toEqual('1m0s0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- MINUTE + SECOND + MILLISECOND,
- ),
- true,
- ),
- ).toEqual('1m1s1ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- MINUTE + SECOND + MILLISECOND + 1n,
- ),
- true,
- ),
- ).toEqual('1m1s1ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- MINUTE + SECOND + MILLISECOND + 1n,
- ),
- false,
- ),
- ).toEqual('1m1s1ms1ns');
-
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(HOUR - 1n),
- true,
- ),
- ).toEqual('59m59s999ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(HOUR - 1n),
- false,
- ),
- ).toEqual('59m59s999ms999999ns');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(HOUR),
- true,
- ),
- ).toEqual('1h0m0s0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- HOUR + MINUTE + SECOND + MILLISECOND,
- ),
- true,
- ),
- ).toEqual('1h1m1s1ms');
-
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(DAY - 1n),
- true,
- ),
- ).toEqual('23h59m59s999ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(DAY),
- true,
- ),
- ).toEqual('1d0h0m0s0ms');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- DAY + HOUR + MINUTE + SECOND + MILLISECOND,
- ),
- true,
- ),
- ).toEqual('1d1h1m1s1ms');
- });
-
- it('humanElapsedToNanoseconds', () => {
- expect(TimestampUtils.parseHumanElapsed('0ns')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- );
- expect(TimestampUtils.parseHumanElapsed('1000ns')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1000n),
- );
- expect(TimestampUtils.parseHumanElapsed('0ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- );
- expect(TimestampUtils.parseHumanElapsed('1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MILLISECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('10ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n * MILLISECOND),
- );
-
- expect(TimestampUtils.parseHumanElapsed('999ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(999n * MILLISECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('1s')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('1s0ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('1s0ms0ns')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('1s0ms1ns')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND + 1n),
- );
- expect(TimestampUtils.parseHumanElapsed('0d1s1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(SECOND + MILLISECOND),
- );
-
- expect(TimestampUtils.parseHumanElapsed('1m0s0ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(MINUTE),
- );
- expect(TimestampUtils.parseHumanElapsed('1m1s1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- MINUTE + SECOND + MILLISECOND,
- ),
- );
-
- expect(TimestampUtils.parseHumanElapsed('1h0m')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(HOUR),
- );
- expect(TimestampUtils.parseHumanElapsed('1h1m1s1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- HOUR + MINUTE + SECOND + MILLISECOND,
- ),
- );
-
- expect(TimestampUtils.parseHumanElapsed('1d0s1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(DAY + MILLISECOND),
- );
- expect(TimestampUtils.parseHumanElapsed('1d1h1m1s1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- DAY + HOUR + MINUTE + SECOND + MILLISECOND,
- ),
- );
-
- expect(TimestampUtils.parseHumanElapsed('1d')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(DAY),
- );
- expect(TimestampUtils.parseHumanElapsed('1d1ms')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(DAY + MILLISECOND),
- );
- });
-
- it('humanToNanoseconds throws on invalid input format', () => {
- const invalidFormatError = new Error('Invalid elapsed timestamp format');
- expect(() => TimestampUtils.parseHumanElapsed('1d1h1m1s0ns1ms')).toThrow(
- invalidFormatError,
- );
- expect(() => TimestampUtils.parseHumanElapsed('1dns')).toThrow(
- invalidFormatError,
- );
- expect(() => TimestampUtils.parseHumanElapsed('100')).toThrow(
- invalidFormatError,
- );
- expect(() => TimestampUtils.parseHumanElapsed('')).toThrow(
- invalidFormatError,
- );
- });
-
- it('nanosecondsToHumanReal', () => {
- const NOV_10_2022 = 1668038400000n * MILLISECOND;
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
- true,
- ),
- ).toEqual('1970-01-01T00:00:00.000');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
- NOV_10_2022 +
- 22n * HOUR +
- 4n * MINUTE +
- 54n * SECOND +
- 186n * MILLISECOND +
- 123212n,
- ),
- true,
- ),
- ).toEqual('2022-11-10T22:04:54.186');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(NOV_10_2022),
- true,
- ),
- ).toEqual('2022-11-10T00:00:00.000');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(NOV_10_2022 + 1n),
- true,
- ),
- ).toEqual('2022-11-10T00:00:00.000');
-
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
- false,
- ),
- ).toEqual('1970-01-01T00:00:00.000000000');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
- NOV_10_2022 +
- 22n * HOUR +
- 4n * MINUTE +
- 54n * SECOND +
- 186n * MILLISECOND +
- 123212n,
- ),
- false,
- ),
- ).toEqual('2022-11-10T22:04:54.186123212');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(NOV_10_2022),
- false,
- ),
- ).toEqual('2022-11-10T00:00:00.000000000');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(NOV_10_2022 + 1n),
- false,
- ),
- ).toEqual('2022-11-10T00:00:00.000000001');
- });
-
- it('humanRealToNanoseconds', () => {
- const NOV_10_2022 = 1668038400000n * MILLISECOND;
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T22:04:54.186123212'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
- NOV_10_2022 +
- 22n * HOUR +
- 4n * MINUTE +
- 54n * SECOND +
- 186n * MILLISECOND +
- 123212n,
- ),
- );
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T22:04:54.186123212Z'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668117894186123212n),
- );
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T22:04:54.186000212'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668117894186000212n),
- );
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T22:04:54.006000002'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668117894006000002n),
- );
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T06:04:54.006000002'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668060294006000002n),
- );
- expect(TimestampUtils.parseHumanReal('2022-11-10T06:04:54')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668060294000000000n),
- );
- expect(TimestampUtils.parseHumanReal('2022-11-10T06:04:54.0')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668060294000000000n),
- );
- expect(TimestampUtils.parseHumanReal('2022-11-10T06:04:54.0100')).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668060294010000000n),
- );
- expect(
- TimestampUtils.parseHumanReal('2022-11-10T06:04:54.0175328'),
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668060294017532800n),
- );
- });
-
- it('canReverseDateFormatting', () => {
- let timestamp =
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1668117894186123212n);
- expect(
- TimestampUtils.parseHumanReal(TimestampUtils.format(timestamp)),
- ).toEqual(timestamp);
-
- timestamp = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- DAY + HOUR + MINUTE + SECOND + MILLISECOND + 1n,
- );
- expect(
- TimestampUtils.parseHumanElapsed(TimestampUtils.format(timestamp)),
- ).toEqual(timestamp);
- });
-
- it('humanToNanoseconds throws on invalid input format', () => {
- const invalidFormatError = new Error('Invalid real timestamp format');
- expect(() => TimestampUtils.parseHumanReal('23h59m59s999ms5ns')).toThrow(
- invalidFormatError,
- );
- expect(() => TimestampUtils.parseHumanReal('1d')).toThrow(
- invalidFormatError,
- );
- expect(() => TimestampUtils.parseHumanReal('100')).toThrow(
- invalidFormatError,
- );
- expect(() =>
- TimestampUtils.parseHumanReal('06h4m54s, 10 Nov 2022'),
- ).toThrow(invalidFormatError);
- expect(() => TimestampUtils.parseHumanReal('')).toThrow(invalidFormatError);
- expect(() => TimestampUtils.parseHumanReal('2022-11-10T06:04:54.')).toThrow(
- invalidFormatError,
- );
- expect(() =>
- TimestampUtils.parseHumanReal('2022-11-10T06:04:54.1234567890'),
- ).toThrow(invalidFormatError);
- });
-
it('nano second regex accept all expected inputs', () => {
expect(TimestampUtils.NS_TIMESTAMP_REGEX.test('123')).toBeTrue();
expect(TimestampUtils.NS_TIMESTAMP_REGEX.test('123ns')).toBeTrue();
@@ -463,26 +61,4 @@
expect(TimestampUtils.NS_TIMESTAMP_REGEX.test('a123 ns')).toBeFalse();
expect(TimestampUtils.NS_TIMESTAMP_REGEX.test('')).toBeFalse();
});
-
- it('format real', () => {
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n, 500n),
- ),
- ).toEqual('1970-01-01T00:00:00.000000600');
- expect(
- TimestampUtils.format(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(100n * MILLISECOND, 500n),
- true,
- ),
- ).toEqual('1970-01-01T00:00:00.100');
- });
-
- it('format elapsed', () => {
- const timestamp = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- 100n * MILLISECOND,
- );
- expect(TimestampUtils.format(timestamp, true)).toEqual('100ms');
- expect(TimestampUtils.format(timestamp)).toEqual('100ms0ns');
- });
});
diff --git a/tools/winscope/src/common/utc_offset.ts b/tools/winscope/src/common/utc_offset.ts
new file mode 100644
index 0000000..4a903c3
--- /dev/null
+++ b/tools/winscope/src/common/utc_offset.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TIME_UNIT_TO_NANO} from './time_units';
+
+export class UTCOffset {
+ private valueNs: bigint | undefined;
+
+ getValueNs(): bigint | undefined {
+ return this.valueNs;
+ }
+
+ format(): string {
+ if (this.valueNs === undefined) {
+ return 'UTC+00:00';
+ }
+ const valueHours = Number(this.valueNs / BigInt(TIME_UNIT_TO_NANO.m)) / 60;
+ const valueHoursAbs = Math.abs(valueHours);
+ const hh = Math.floor(valueHoursAbs);
+ const mm = (valueHoursAbs - hh) * 60;
+ const timeDiff = `${hh}`.padStart(2, '0') + ':' + `${mm}`.padStart(2, '0');
+ return `UTC${this.valueNs < 0 ? '-' : '+'}${timeDiff}`;
+ }
+
+ initialize(valueNs: bigint) {
+ if (valueNs > BigInt(14 * TIME_UNIT_TO_NANO.h)) {
+ console.warn('Failed to set timezone offset greater than UTC+14:00');
+ return;
+ }
+ if (valueNs < BigInt(-12 * TIME_UNIT_TO_NANO.h)) {
+ console.warn('Failed to set timezone offset greater than UTC-12:00');
+ return;
+ }
+ this.valueNs = valueNs;
+ }
+}
diff --git a/tools/winscope/src/common/utc_offset_test.ts b/tools/winscope/src/common/utc_offset_test.ts
new file mode 100644
index 0000000..568a719
--- /dev/null
+++ b/tools/winscope/src/common/utc_offset_test.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TIME_UNIT_TO_NANO} from './time_units';
+import {UTCOffset} from './utc_offset';
+
+describe('UTCOffset', () => {
+ const utcOffset = new UTCOffset();
+
+ it('sets positive offset for whole single-digit number hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * 2));
+ expect(utcOffset.format()).toEqual('UTC+02:00');
+ });
+
+ it('sets positive offset for whole double-digit number hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * 11));
+ expect(utcOffset.format()).toEqual('UTC+11:00');
+ });
+
+ it('sets positive offset for fractional hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * 5.5));
+ expect(utcOffset.format()).toEqual('UTC+05:30');
+ });
+
+ it('sets negative offset for whole single-digit number hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * -8));
+ expect(utcOffset.format()).toEqual('UTC-08:00');
+ });
+
+ it('sets negative offset for whole double-digit number hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * -10));
+ expect(utcOffset.format()).toEqual('UTC-10:00');
+ });
+
+ it('sets negative offset for fractional hours', () => {
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * -4.5));
+ expect(utcOffset.format()).toEqual('UTC-04:30');
+ });
+
+ it('does not set offset for invalid value', () => {
+ const utcOffset = new UTCOffset();
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * 15)); // later than UTC+14:00
+ expect(utcOffset.getValueNs()).toBeUndefined();
+ utcOffset.initialize(BigInt(TIME_UNIT_TO_NANO.h * -13)); // earlier than UTC-12:00
+ expect(utcOffset.getValueNs()).toBeUndefined();
+ });
+});
diff --git a/tools/winscope/src/cross_tool/cross_tool_protocol.ts b/tools/winscope/src/cross_tool/cross_tool_protocol.ts
index 3e48e4d..d27bbce 100644
--- a/tools/winscope/src/cross_tool/cross_tool_protocol.ts
+++ b/tools/winscope/src/cross_tool/cross_tool_protocol.ts
@@ -15,7 +15,7 @@
*/
import {FunctionUtils} from 'common/function_utils';
-import {TimestampType} from 'common/time';
+import {RemoteToolTimestampConverter} from 'common/timestamp_converter';
import {
RemoteToolFilesReceived,
RemoteToolTimestampReceived,
@@ -46,6 +46,7 @@
{
private remoteTool?: RemoteTool;
private emitEvent: EmitEvent = FunctionUtils.DO_NOTHING_ASYNC;
+ private timestampConverter: RemoteToolTimestampConverter | undefined;
constructor() {
window.addEventListener('message', async (event) => {
@@ -57,6 +58,10 @@
this.emitEvent = callback;
}
+ setTimestampConverter(converter: RemoteToolTimestampConverter | undefined) {
+ this.timestampConverter = converter;
+ }
+
async onWinscopeEvent(event: WinscopeEvent) {
await event.visit(
WinscopeEventType.TRACE_POSITION_UPDATE,
@@ -65,13 +70,11 @@
return;
}
- const timestamp = event.position.timestamp;
- if (timestamp.getType() !== TimestampType.REAL) {
- console.warn(
- 'Cannot propagate timestamp change to remote tool.' +
- ` Remote tool expects timestamp type ${TimestampType.REAL},` +
- ` but Winscope wants to notify timestamp type ${timestamp.getType()}.`,
+ const timestamp =
+ this.timestampConverter?.tryMakeTimestampForRemoteTool(
+ event.position.timestamp,
);
+ if (timestamp === undefined) {
return;
}
diff --git a/tools/winscope/src/messaging/user_warnings.ts b/tools/winscope/src/messaging/user_warnings.ts
index 8f2cdac..59b8410 100644
--- a/tools/winscope/src/messaging/user_warnings.ts
+++ b/tools/winscope/src/messaging/user_warnings.ts
@@ -15,8 +15,7 @@
*/
import {TimeRange} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
-import {TimestampUtils} from 'common/timestamp_utils';
+import {TimeDuration} from 'common/time_duration';
import {TraceType} from 'trace/trace_type';
import {UserWarning} from './user_warning';
@@ -34,16 +33,6 @@
}
}
-export class NoCommonTimestampType extends UserWarning {
- getDescriptor(): string {
- return 'no common timestamp';
- }
-
- getMessage(): string {
- return 'Failed to load traces because no common timestamp type could be found';
- }
-}
-
export class NoInputFiles extends UserWarning {
getDescriptor(): string {
return 'no input';
@@ -57,7 +46,7 @@
export class TraceHasOldData extends UserWarning {
constructor(
private readonly descriptor: string,
- private readonly timeGap: TimeRange,
+ private readonly timeGap?: TimeRange,
) {
super();
}
@@ -67,15 +56,15 @@
}
getMessage(): string {
- const elapsedTime = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- this.timeGap.to.getValueNs() - this.timeGap.from.getValueNs(),
+ const elapsedTime = this.timeGap
+ ? new TimeDuration(
+ this.timeGap.to.getValueNs() - this.timeGap.from.getValueNs(),
+ )
+ : undefined;
+ return (
+ `${this.descriptor}: discarded because data is old` +
+ (this.timeGap ? `der than ${elapsedTime?.format(true)}` : '')
);
- return `${
- this.descriptor
- }: discarded because data is older than ${TimestampUtils.format(
- elapsedTime,
- true,
- )}`;
}
}
diff --git a/tools/winscope/src/parsers/events/parser_eventlog.ts b/tools/winscope/src/parsers/events/parser_eventlog.ts
index 7f18979..6e5923e 100644
--- a/tools/winscope/src/parsers/events/parser_eventlog.ts
+++ b/tools/winscope/src/parsers/events/parser_eventlog.ts
@@ -15,7 +15,7 @@
*/
import {StringUtils} from 'common/string_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {TraceType} from 'trace/trace_type';
import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto';
@@ -35,6 +35,14 @@
return ParserEventLog.MAGIC_NUMBER;
}
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(buffer: Uint8Array): Event[] {
const decodedLogs = this.decodeByteArray(buffer);
const events = this.parseLogs(decodedLogs);
@@ -43,21 +51,13 @@
});
}
- override getTimestamp(
- type: TimestampType,
- entry: Event,
- ): undefined | Timestamp {
- if (type === TimestampType.REAL) {
- return this.timestampFactory.makeRealTimestamp(entry.eventTimestamp);
- }
- return undefined;
+ protected override getTimestamp(entry: Event): Timestamp {
+ return this.timestampConverter.makeTimestampFromRealNs(
+ entry.eventTimestamp,
+ );
}
- override processDecodedEntry(
- index: number,
- timestampType: TimestampType,
- entry: Event,
- ): PropertyTreeNode {
+ override processDecodedEntry(index: number, entry: Event): PropertyTreeNode {
return new PropertyTreeBuilderFromProto()
.setData(entry)
.setRootId('EventLogTrace')
diff --git a/tools/winscope/src/parsers/events/parser_eventlog_test.ts b/tools/winscope/src/parsers/events/parser_eventlog_test.ts
index 6e14bf3..04e2f01 100644
--- a/tools/winscope/src/parsers/events/parser_eventlog_test.ts
+++ b/tools/winscope/src/parsers/events/parser_eventlog_test.ts
@@ -15,9 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -45,26 +44,20 @@
});
it('has expected timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.REAL),
- );
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(184);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047981157174n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047991161039n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047991310494n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047981157174n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047991161039n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047991310494n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
- it("doesn't provide elapsed timestamps", () => {
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(undefined);
- });
-
it('contains parsed jank CUJ events', async () => {
- const entry = await parser.getEntry(18, TimestampType.REAL);
+ const entry = await parser.getEntry(18);
const expected = new PropertyTreeBuilder()
.setRootId('EventLogTrace')
@@ -85,27 +78,6 @@
expect(entry).toEqual(expected);
});
-
- it('applies timezone info to real timestamps', async () => {
- const parserWithTimezoneInfo = assertDefined(
- await UnitTestUtils.getParser('traces/eventlog.winscope', true),
- ) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.EVENT_LOG,
- );
-
- const timestamps = assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- );
- expect(timestamps.length).toEqual(184);
-
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847981157174n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847991161039n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847991310494n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
});
describe('trace with timestamps not monotonically increasing', () => {
@@ -121,22 +93,20 @@
});
it('sorts entries to make timestamps monotonically increasing', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.REAL),
- );
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(3);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047981157174n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047991161039n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207047991310494n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047981157174n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047991161039n),
+ TimestampConverterUtils.makeRealTimestamp(1681207047991310494n),
];
expect(timestamps).toEqual(expected);
});
it('contains parsed events', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
const expected = new PropertyTreeBuilder()
.setRootId('EventLogTrace')
@@ -154,29 +124,5 @@
expect(entry).toEqual(expected);
});
-
- it('applies timezone info to real timestamps', async () => {
- const parserWithTimezoneInfo = assertDefined(
- await UnitTestUtils.getParser(
- 'traces/eventlog_timestamps_not_monotonically_increasing.winscope',
- true,
- ),
- ) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.EVENT_LOG,
- );
-
- const timestamps = assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- );
- expect(timestamps.length).toEqual(3);
-
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847981157174n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847991161039n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681226847991310494n),
- ];
- expect(timestamps).toEqual(expected);
- });
});
});
diff --git a/tools/winscope/src/parsers/events/traces_parser_cujs.ts b/tools/winscope/src/parsers/events/traces_parser_cujs.ts
index d25809c..677595d 100644
--- a/tools/winscope/src/parsers/events/traces_parser_cujs.ts
+++ b/tools/winscope/src/parsers/events/traces_parser_cujs.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AbstractTracesParser} from 'parsers/legacy/abstract_traces_parser';
import {Trace} from 'trace/trace';
import {Traces} from 'trace/traces';
@@ -33,8 +33,8 @@
private readonly descriptors: string[];
private decodedEntries: PropertyTreeNode[] | undefined;
- constructor(traces: Traces) {
- super();
+ constructor(traces: Traces, timestampConverter: ParserTimestampConverter) {
+ super(timestampConverter);
const eventlogTrace = traces.getTrace(TraceType.EVENT_LOG);
if (eventlogTrace !== undefined) {
@@ -63,17 +63,14 @@
);
});
this.decodedEntries = this.makeCujsFromEvents(cujEvents);
- await this.parseTimestamps();
+ await this.createTimestamps();
}
getLengthEntries(): number {
return assertDefined(this.decodedEntries).length;
}
- getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<PropertyTreeNode> {
+ getEntry(index: number): Promise<PropertyTreeNode> {
const entry = assertDefined(this.decodedEntries)[index];
return Promise.resolve(entry);
}
@@ -86,22 +83,18 @@
return TraceType.CUJS;
}
- override getTimestamp(
- type: TimestampType,
- cuj: PropertyTreeNode,
- ): undefined | Timestamp {
+ protected override getTimestamp(cuj: PropertyTreeNode): Timestamp {
const cujTimestamp = assertDefined(cuj.getChildByName('startTimestamp'));
- if (type === TimestampType.ELAPSED) {
- const elapsedNanos = assertDefined(
- cujTimestamp.getChildByName('elapsedNanos'),
- ).getValue();
- return NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(elapsedNanos);
- } else if (type === TimestampType.REAL) {
- const unixNanos = assertDefined(
- cujTimestamp.getChildByName('unixNanos'),
- ).getValue();
- return NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(unixNanos);
- }
+ return this.timestampConverter.makeTimestampFromRealNs(
+ assertDefined(cujTimestamp.getChildByName('unixNanos')).getValue(),
+ );
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
return undefined;
}
diff --git a/tools/winscope/src/parsers/events/traces_parser_cujs_test.ts b/tools/winscope/src/parsers/events/traces_parser_cujs_test.ts
index 11d489f..724bde4 100644
--- a/tools/winscope/src/parsers/events/traces_parser_cujs_test.ts
+++ b/tools/winscope/src/parsers/events/traces_parser_cujs_test.ts
@@ -15,9 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
@@ -28,6 +27,7 @@
let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
parser = (await UnitTestUtils.getTracesParser([
'traces/eventlog.winscope',
])) as Parser<PropertyTreeNode>;
@@ -37,37 +37,20 @@
expect(parser.getTraceType()).toEqual(TraceType.CUJS);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
-
- expect(timestamps.length).toEqual(16);
-
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2661012770462n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2661012874914n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2661012903966n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207048025446000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207048025551000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1681207048025580000n),
+ TimestampConverterUtils.makeRealTimestamp(1681207048025446000n),
+ TimestampConverterUtils.makeRealTimestamp(1681207048025551000n),
+ TimestampConverterUtils.makeRealTimestamp(1681207048025580000n),
];
- const timestamps = parser.getTimestamps(TimestampType.REAL)!;
-
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(16);
-
expect(timestamps.slice(0, 3)).toEqual(expected);
});
it('contains parsed CUJ events', async () => {
- const entry = await parser.getEntry(2, TimestampType.REAL);
+ const entry = await parser.getEntry(2);
const expected = new PropertyTreeBuilder()
.setRootId('CujTrace')
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients.ts
index 49f515e..1544b73 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {HierarchyTreeBuilderInputMethod} from 'parsers/input_method/hierarchy_tree_builder_input_method';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {AddDefaults} from 'parsers/operations/add_defaults';
@@ -86,7 +86,7 @@
),
};
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.INPUT_METHOD_CLIENTS;
@@ -96,6 +96,14 @@
return ParserInputMethodClients.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): android.view.inputmethod.IInputMethodClientsTraceProto[] {
@@ -106,35 +114,20 @@
const timeOffset = BigInt(
decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
return decoded.entry ?? [];
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: android.view.inputmethod.IInputMethodClientsTraceProto,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: android.view.inputmethod.IInputMethodClientsTraceProto,
): HierarchyTreeNode {
if (
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients_test.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients_test.ts
index fb51b8e..7f32568 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients_test.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_clients_test.ts
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -23,7 +23,7 @@
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
describe('ParserInputMethodClients', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -41,70 +41,25 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- expect(parser.getTimestamps(TimestampType.ELAPSED)!.length).toEqual(13);
-
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15613638434n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15647516364n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15677650967n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090215405395n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090249283325n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090279417928n),
];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090215405395n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090249283325n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090279417928n),
- ];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.REAL)).slice(0, 3),
- ).toEqual(expected);
+ expect(assertDefined(parser.getTimestamps()).slice(0, 3)).toEqual(
+ expected,
+ );
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ const entry = await parser.getEntry(1);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodClients entry');
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/InputMethodClients.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_CLIENTS,
- );
-
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15613638434n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15647516364n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15677650967n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(expectedElapsed);
-
- const expectedReal = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126890215405395n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126890249283325n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126890279417928n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- ).slice(0, 3),
- ).toEqual(expectedReal);
- });
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -118,40 +73,16 @@
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
});
- it('provides elapsed timestamps', () => {
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED))[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149083651642n),
+ it('provides timestamps', () => {
+ expect(assertDefined(parser.getTimestamps())[0]).toEqual(
+ TimestampConverterUtils.makeElapsedTimestamp(1149083651642n),
);
});
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toBeUndefined();
- });
-
- it('retrieves trace entry from elapsed timestamp', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ it('retrieves trace entry from timestamp', async () => {
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodClients entry');
});
-
- it('does not apply timezone info to elapsed timestamps', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/InputMethodClients.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_CLIENTS,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149083651642n),
- );
- });
});
});
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service.ts
index 2daa57f..51d73c9 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {HierarchyTreeBuilderInputMethod} from 'parsers/input_method/hierarchy_tree_builder_input_method';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {AddDefaults} from 'parsers/operations/add_defaults';
@@ -92,7 +92,7 @@
),
};
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.INPUT_METHOD_MANAGER_SERVICE;
@@ -102,6 +102,14 @@
return ParserInputMethodManagerService.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): android.view.inputmethod.IInputMethodManagerServiceTraceProto[] {
@@ -112,35 +120,20 @@
const timeOffset = BigInt(
decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
return decoded.entry ?? [];
}
protected override getTimestamp(
- type: TimestampType,
entry: android.view.inputmethod.IInputMethodManagerServiceTraceProto,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
protected override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: android.view.inputmethod.IInputMethodManagerServiceTraceProto,
): HierarchyTreeNode {
if (
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service_test.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service_test.ts
index 7e767b9..d430198 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service_test.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_manager_service_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -23,7 +22,7 @@
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
describe('ParserInputMethodManagerService', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -42,46 +41,20 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15963782518n),
- ]);
- });
-
- it('provides real timestamps', () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090565549479n),
+ it('provides timestamps', () => {
+ expect(parser.getTimestamps()).toEqual([
+ TimestampConverterUtils.makeRealTimestamp(1659107090565549479n),
]);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodManagerService entry');
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/InputMethodManagerService.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_MANAGER_SERVICE,
- );
-
- expect(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15963782518n),
- ]);
-
- expect(parserWithTimezoneInfo.getTimestamps(TimestampType.REAL)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126890565549479n),
- ]);
- });
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -96,40 +69,16 @@
);
});
- it('provides elapsed timestamps', () => {
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED))[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149226290110n),
+ it('provides timestamps', () => {
+ expect(assertDefined(parser.getTimestamps())[0]).toEqual(
+ TimestampConverterUtils.makeElapsedTimestamp(1149226290110n),
);
});
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
- });
-
- it('retrieves trace entry from elapsed timestamp', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ it('retrieves trace entry from timestamp', async () => {
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodManagerService entry');
});
-
- it('does not apply timezone info to elapsed timestamps', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/InputMethodManagerService.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_MANAGER_SERVICE,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149226290110n),
- );
- });
});
});
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service.ts
index 7d7c415..631d4a1 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {HierarchyTreeBuilderInputMethod} from 'parsers/input_method/hierarchy_tree_builder_input_method';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {AddDefaults} from 'parsers/operations/add_defaults';
@@ -86,7 +86,7 @@
),
};
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.INPUT_METHOD_SERVICE;
@@ -96,6 +96,14 @@
return ParserInputMethodService.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): android.view.inputmethod.IInputMethodServiceTraceProto[] {
@@ -106,35 +114,20 @@
const timeOffset = BigInt(
decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
return decoded.entry ?? [];
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: android.view.inputmethod.IInputMethodServiceTraceProto,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: android.view.inputmethod.IInputMethodServiceTraceProto,
): HierarchyTreeNode {
if (
diff --git a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service_test.ts b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service_test.ts
index 5a6ad7a..e8b3991 100644
--- a/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service_test.ts
+++ b/tools/winscope/src/parsers/input_method/legacy/parser_input_method_service_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -23,7 +22,7 @@
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
describe('ParserInputMethodService', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -36,52 +35,26 @@
it('has expected trace type', () => {
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
});
+
it('has expected coarse version', () => {
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(16578752896n),
+ TimestampConverterUtils.makeRealTimestamp(1659107091180519857n),
];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107091180519857n),
- ];
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ expect(parser.getTimestamps()).toEqual(expected);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodService entry');
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/InputMethodService.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_SERVICE,
- );
-
- expect(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(16578752896n),
- ]);
-
- expect(parserWithTimezoneInfo.getTimestamps(TimestampType.REAL)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126891180519857n),
- ]);
- });
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -94,40 +67,16 @@
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_SERVICE);
});
- it('provides elapsed timestamps', () => {
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED))[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149230019887n),
+ it('provides timestamps', () => {
+ expect(assertDefined(parser.getTimestamps())[0]).toEqual(
+ TimestampConverterUtils.makeElapsedTimestamp(1149230019887n),
);
});
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
- });
-
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('InputMethodService entry');
});
-
- it('does not apply timezone info to elapsed timestamps', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/InputMethodService.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.INPUT_METHOD_SERVICE,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1149230019887n),
- );
- });
});
});
diff --git a/tools/winscope/src/parsers/legacy/abstract_parser.ts b/tools/winscope/src/parsers/legacy/abstract_parser.ts
index 7daa776..cf14d45 100644
--- a/tools/winscope/src/parsers/legacy/abstract_parser.ts
+++ b/tools/winscope/src/parsers/legacy/abstract_parser.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {CoarseVersion} from 'trace/coarse_version';
import {
CustomQueryParamTypeMap,
@@ -31,26 +31,19 @@
export abstract class AbstractParser<T extends object = object>
implements Parser<T>
{
- private timestamps = new Map<TimestampType, Timestamp[]>();
+ private timestamps: Timestamp[] | undefined;
protected traceFile: TraceFile;
protected decodedEntries: any[] = [];
- protected timestampFactory: TimestampFactory;
+ protected timestampConverter: ParserTimestampConverter;
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,
- timestampType: TimestampType,
- decodedEntry: any,
- ): any;
+ protected abstract getTimestamp(decodedEntry: any): Timestamp;
+ protected abstract processDecodedEntry(index: number, decodedEntry: any): any;
- constructor(trace: TraceFile, timestampFactory: TimestampFactory) {
+ constructor(trace: TraceFile, timestampConverter: ParserTimestampConverter) {
this.traceFile = trace;
- this.timestampFactory = timestampFactory;
+ this.timestampConverter = timestampConverter;
}
async parse() {
@@ -60,7 +53,6 @@
this.getMagicNumber(),
);
this.decodedEntries = this.decodeTrace(traceBuffer);
- this.timestamps = this.decodeTimestamps();
}
getDescriptors(): string[] {
@@ -71,23 +63,20 @@
return this.decodedEntries.length;
}
- getTimestamps(type: TimestampType): undefined | Timestamp[] {
- return this.timestamps.get(type);
+ createTimestamps() {
+ this.timestamps = this.decodeTimestamps();
+ }
+
+ getTimestamps(): undefined | Timestamp[] {
+ return this.timestamps;
}
getCoarseVersion(): CoarseVersion {
return CoarseVersion.LEGACY;
}
- getEntry(
- index: AbsoluteEntryIndex,
- timestampType: TimestampType,
- ): Promise<T> {
- const entry = this.processDecodedEntry(
- index,
- timestampType,
- this.decodedEntries[index],
- );
+ getEntry(index: AbsoluteEntryIndex): Promise<T> {
+ const entry = this.processDecodedEntry(index, this.decodedEntries[index]);
return Promise.resolve(entry);
}
@@ -99,27 +88,11 @@
throw new Error('Not implemented');
}
- private decodeTimestamps(): Map<TimestampType, Timestamp[]> {
- const timeStampMap = new Map<TimestampType, Timestamp[]>();
- for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
- const timestamps: Timestamp[] = [];
- let areTimestampsValid = true;
-
- for (const entry of this.decodedEntries) {
- const timestamp = this.getTimestamp(type, entry);
- if (timestamp === undefined) {
- areTimestampsValid = false;
- break;
- }
- timestamps.push(timestamp);
- }
-
- if (areTimestampsValid) {
- timeStampMap.set(type, timestamps);
- }
- }
- return timeStampMap;
+ private decodeTimestamps(): Timestamp[] {
+ return this.decodedEntries.map((entry) => this.getTimestamp(entry));
}
abstract getTraceType(): TraceType;
+ abstract getRealToBootTimeOffsetNs(): bigint | undefined;
+ abstract getRealToMonotonicTimeOffsetNs(): bigint | undefined;
}
diff --git a/tools/winscope/src/parsers/legacy/abstract_traces_parser.ts b/tools/winscope/src/parsers/legacy/abstract_traces_parser.ts
index ac02f5c..3194c89 100644
--- a/tools/winscope/src/parsers/legacy/abstract_traces_parser.ts
+++ b/tools/winscope/src/parsers/legacy/abstract_traces_parser.ts
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {CoarseVersion} from 'trace/coarse_version';
import {
CustomQueryParserResultTypeMap,
@@ -25,18 +26,14 @@
import {TraceType} from 'trace/trace_type';
export abstract class AbstractTracesParser<T> implements Parser<T> {
- private timestamps = new Map<TimestampType, Timestamp[]>();
+ private timestamps: Timestamp[] | undefined;
+ protected timestampConverter: ParserTimestampConverter;
- abstract parse(): Promise<void>;
+ protected abstract getTimestamp(decodedEntry: any): Timestamp;
- abstract getDescriptors(): string[];
-
- abstract getTraceType(): TraceType;
-
- abstract getEntry(
- index: AbsoluteEntryIndex,
- timestampType: TimestampType,
- ): Promise<T>;
+ constructor(timestampConverter: ParserTimestampConverter) {
+ this.timestampConverter = timestampConverter;
+ }
getCoarseVersion(): CoarseVersion {
return CoarseVersion.LEGACY;
@@ -49,35 +46,29 @@
throw new Error('Not implemented');
}
- abstract getLengthEntries(): number;
-
- getTimestamps(type: TimestampType): Timestamp[] | undefined {
- return this.timestamps.get(type);
+ getTimestamps(): Timestamp[] | undefined {
+ return this.timestamps;
}
- protected async parseTimestamps() {
- for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
- const timestamps: Timestamp[] = [];
- let areTimestampsValid = true;
+ async createTimestamps() {
+ this.timestamps = await this.decodeTimestamps();
+ }
- for (let index = 0; index < this.getLengthEntries(); index++) {
- const entry = await this.getEntry(index, type);
- const timestamp = this.getTimestamp(type, entry);
- if (timestamp === undefined) {
- areTimestampsValid = false;
- break;
- }
- timestamps.push(timestamp);
- }
-
- if (areTimestampsValid) {
- this.timestamps.set(type, timestamps);
- }
+ private async decodeTimestamps() {
+ const timestampsNs = [];
+ for (let index = 0; index < this.getLengthEntries(); index++) {
+ const entry = await this.getEntry(index);
+ const timestamp = this.getTimestamp(entry);
+ timestampsNs.push(timestamp);
}
+ return timestampsNs;
}
- protected abstract getTimestamp(
- type: TimestampType,
- decodedEntry: any,
- ): undefined | Timestamp;
+ abstract parse(): Promise<void>;
+ abstract getDescriptors(): string[];
+ abstract getTraceType(): TraceType;
+ abstract getEntry(index: AbsoluteEntryIndex): Promise<T>;
+ abstract getLengthEntries(): number;
+ abstract getRealToMonotonicTimeOffsetNs(): bigint | undefined;
+ abstract getRealToBootTimeOffsetNs(): bigint | undefined;
}
diff --git a/tools/winscope/src/parsers/legacy/parser_common_test.ts b/tools/winscope/src/parsers/legacy/parser_common_test.ts
index 76213ec..48945db 100644
--- a/tools/winscope/src/parsers/legacy/parser_common_test.ts
+++ b/tools/winscope/src/parsers/legacy/parser_common_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceFile} from 'trace/trace_file';
@@ -24,6 +23,10 @@
import {ParserFactory} from './parser_factory';
describe('Parser', () => {
+ beforeAll(() => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
+ });
+
it('is robust to empty trace file', async () => {
const trace = new TraceFile(
await UnitTestUtils.getFixtureFile('traces/empty.pb'),
@@ -31,7 +34,7 @@
);
const parsers = await new ParserFactory().createParsers(
[trace],
- NO_TIMEZONE_OFFSET_FACTORY,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER,
);
expect(parsers.length).toEqual(0);
});
@@ -42,8 +45,7 @@
);
expect(parser.getTraceType()).toEqual(TraceType.INPUT_METHOD_CLIENTS);
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual([]);
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual([]);
+ expect(parser.getTimestamps()).toEqual([]);
});
describe('real timestamp', () => {
@@ -57,25 +59,22 @@
it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089075566202n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089999048990n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090010194213n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089075566202n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089999048990n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090010194213n),
];
- expect(parser.getTimestamps(TimestampType.REAL)!.slice(0, 3)).toEqual(
+ expect(assertDefined(parser.getTimestamps()).slice(0, 3)).toEqual(
expected,
);
});
it('retrieves trace entries', async () => {
- let entry = await parser.getEntry(0, TimestampType.REAL);
+ let entry = await parser.getEntry(0);
expect(
assertDefined(entry.getEagerPropertyByName('focusedApp')).getValue(),
).toEqual('com.google.android.apps.nexuslauncher/.NexusLauncherActivity');
- entry = await parser.getEntry(
- parser.getLengthEntries() - 1,
- TimestampType.REAL,
- );
+ entry = await parser.getEntry(parser.getLengthEntries() - 1);
expect(
assertDefined(entry.getEagerPropertyByName('focusedApp')).getValue(),
).toEqual('com.google.android.apps.nexuslauncher/.NexusLauncherActivity');
@@ -93,23 +92,20 @@
it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850254319343n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850763506110n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850782750048n),
+ TimestampConverterUtils.makeElapsedTimestamp(850254319343n),
+ TimestampConverterUtils.makeElapsedTimestamp(850763506110n),
+ TimestampConverterUtils.makeElapsedTimestamp(850782750048n),
];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
+ expect(parser.getTimestamps()).toEqual(expected);
});
it('retrieves trace entries', async () => {
- let entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ let entry = await parser.getEntry(0);
expect(
assertDefined(entry.getEagerPropertyByName('focusedApp')).getValue(),
).toEqual('com.google.android.apps.nexuslauncher/.NexusLauncherActivity');
- entry = await parser.getEntry(
- parser.getLengthEntries() - 1,
- TimestampType.ELAPSED,
- );
+ entry = await parser.getEntry(parser.getLengthEntries() - 1);
expect(
assertDefined(entry.getEagerPropertyByName('focusedApp')).getValue(),
).toEqual('com.google.android.apps.nexuslauncher/.NexusLauncherActivity');
diff --git a/tools/winscope/src/parsers/legacy/parser_factory.ts b/tools/winscope/src/parsers/legacy/parser_factory.ts
index c9d1ca4..b829607 100644
--- a/tools/winscope/src/parsers/legacy/parser_factory.ts
+++ b/tools/winscope/src/parsers/legacy/parser_factory.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {ProgressListener} from 'messaging/progress_listener';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
import {UnsupportedFileFormat} from 'messaging/user_warnings';
@@ -57,7 +57,7 @@
async createParsers(
traceFiles: TraceFile[],
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
progressListener?: ProgressListener,
notificationListener?: UserNotificationsListener,
): Promise<FileAndParser[]> {
@@ -73,7 +73,7 @@
for (const ParserType of ParserFactory.PARSERS) {
try {
- const p = new ParserType(traceFile, timestampFactory);
+ const p = new ParserType(traceFile, timestampConverter);
await p.parse();
hasFoundParser = true;
diff --git a/tools/winscope/src/parsers/legacy/traces_parser_factory.ts b/tools/winscope/src/parsers/legacy/traces_parser_factory.ts
index 428e8bb..b8cca79 100644
--- a/tools/winscope/src/parsers/legacy/traces_parser_factory.ts
+++ b/tools/winscope/src/parsers/legacy/traces_parser_factory.ts
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {TracesParserCujs} from 'parsers/events/traces_parser_cujs';
import {TracesParserTransitions} from 'parsers/transitions/legacy/traces_parser_transitions';
import {Parser} from 'trace/parser';
@@ -22,12 +23,15 @@
export class TracesParserFactory {
static readonly PARSERS = [TracesParserCujs, TracesParserTransitions];
- async createParsers(traces: Traces): Promise<Array<Parser<object>>> {
+ async createParsers(
+ traces: Traces,
+ timestampConverter: ParserTimestampConverter,
+ ): Promise<Array<Parser<object>>> {
const parsers: Array<Parser<object>> = [];
for (const ParserType of TracesParserFactory.PARSERS) {
try {
- const parser = new ParserType(traces);
+ const parser = new ParserType(traces, timestampConverter);
await parser.parse();
parsers.push(parser);
} catch (error) {
diff --git a/tools/winscope/src/parsers/operations/transform_to_timestamp_test.ts b/tools/winscope/src/parsers/operations/transform_to_timestamp_test.ts
index dc6ddcd..c769ab1 100644
--- a/tools/winscope/src/parsers/operations/transform_to_timestamp_test.ts
+++ b/tools/winscope/src/parsers/operations/transform_to_timestamp_test.ts
@@ -15,9 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {MockLong} from 'test/unit/mock_long';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {TransformToTimestamp} from './transform_to_timestamp';
@@ -46,7 +46,7 @@
operation.apply(propertyRoot);
expect(
assertDefined(propertyRoot.getChildByName('timestamp')).getValue(),
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n));
+ ).toEqual(TimestampConverterUtils.makeRealTimestamp(10n));
expect(
assertDefined(propertyRoot.getChildByName('otherTimestamp')).getValue(),
).toEqual(longTimestamp);
@@ -60,17 +60,17 @@
operation.apply(propertyRoot);
expect(
assertDefined(propertyRoot.getChildByName('timestamp')).getValue(),
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n));
+ ).toEqual(TimestampConverterUtils.makeElapsedTimestamp(10n));
expect(
assertDefined(propertyRoot.getChildByName('otherTimestamp')).getValue(),
).toEqual(longTimestamp);
});
function makeRealTimestampStrategy(valueNs: bigint) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(valueNs);
+ return TimestampConverterUtils.makeRealTimestamp(valueNs);
}
function makeElapsedTimestampStrategy(valueNs: bigint) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(valueNs);
+ return TimestampConverterUtils.makeElapsedTimestamp(valueNs);
}
});
diff --git a/tools/winscope/src/parsers/perfetto/abstract_parser.ts b/tools/winscope/src/parsers/perfetto/abstract_parser.ts
index 3c5fdb7..75aab1a 100644
--- a/tools/winscope/src/parsers/perfetto/abstract_parser.ts
+++ b/tools/winscope/src/parsers/perfetto/abstract_parser.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined, assertTrue} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {CoarseVersion} from 'trace/coarse_version';
import {
CustomQueryParamTypeMap,
@@ -31,77 +31,70 @@
export abstract class AbstractParser<T> implements Parser<T> {
protected traceProcessor: WasmEngineProxy;
- protected realToElapsedTimeOffsetNs?: bigint;
- protected timestampFactory: TimestampFactory;
- private timestamps = new Map<TimestampType, Timestamp[]>();
+ protected realToBootTimeOffsetNs?: bigint;
+ protected timestampConverter: ParserTimestampConverter;
+ private timestamps: Timestamp[] | undefined;
private lengthEntries = 0;
private traceFile: TraceFile;
+ private elapsedTimestampsNs: Array<bigint> = [];
+
+ protected abstract queryEntry(index: AbsoluteEntryIndex): Promise<any>;
+ protected abstract getTableName(): string;
constructor(
traceFile: TraceFile,
traceProcessor: WasmEngineProxy,
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
) {
this.traceFile = traceFile;
this.traceProcessor = traceProcessor;
- this.timestampFactory = timestampFactory;
+ this.timestampConverter = timestampConverter;
}
async parse() {
- const elapsedTimestamps = await this.queryElapsedTimestamps();
- this.lengthEntries = elapsedTimestamps.length;
+ this.elapsedTimestampsNs = await this.queryElapsedTimestamps();
+ this.lengthEntries = this.elapsedTimestampsNs.length;
assertTrue(
this.lengthEntries > 0,
() =>
`Trace processor tables don't contain entries of type ${this.getTraceType()}`,
);
- this.realToElapsedTimeOffsetNs = await this.queryRealToElapsedTimeOffset(
- assertDefined(elapsedTimestamps.at(-1)),
- );
-
- this.timestamps.set(
- TimestampType.ELAPSED,
- elapsedTimestamps.map((value) =>
- this.timestampFactory.makeElapsedTimestamp(value),
- ),
- );
-
- this.timestamps.set(
- TimestampType.REAL,
- elapsedTimestamps.map((value) =>
- this.timestampFactory.makeRealTimestamp(
- value,
- assertDefined(this.realToElapsedTimeOffsetNs),
- ),
- ),
+ let finalNonZeroNsIndex = -1;
+ for (let i = this.elapsedTimestampsNs.length - 1; i > -1; i--) {
+ if (this.elapsedTimestampsNs[i] !== 0n) {
+ finalNonZeroNsIndex = i;
+ break;
+ }
+ }
+ this.realToBootTimeOffsetNs = await this.queryRealToElapsedTimeOffset(
+ assertDefined(this.elapsedTimestampsNs.at(finalNonZeroNsIndex)),
);
if (this.lengthEntries > 0) {
// Make sure there are trace entries that can be parsed
- await this.getEntry(0, TimestampType.ELAPSED);
+ await this.queryEntry(0);
}
}
- abstract getTraceType(): TraceType;
+ createTimestamps() {
+ this.timestamps = this.elapsedTimestampsNs.map((ns) => {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(ns);
+ });
+ }
getLengthEntries(): number {
return this.lengthEntries;
}
- getTimestamps(type: TimestampType): Timestamp[] | undefined {
- return this.timestamps.get(type);
+ getTimestamps(): Timestamp[] | undefined {
+ return this.timestamps;
}
getCoarseVersion(): CoarseVersion {
return CoarseVersion.LATEST;
}
- abstract getEntry(
- index: AbsoluteEntryIndex,
- timestampType: TimestampType,
- ): Promise<T>;
-
customQuery<Q extends CustomQueryType>(
type: Q,
entriesRange: EntriesRange,
@@ -114,7 +107,13 @@
return [this.traceFile.getDescriptor()];
}
- protected abstract getTableName(): string;
+ getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
private async queryElapsedTimestamps(): Promise<Array<bigint>> {
const sql = `SELECT ts FROM ${this.getTableName()} ORDER BY id;`;
@@ -146,4 +145,7 @@
const real = result.iter({}).get('realtime') as bigint;
return real - elapsedTimestamp;
}
+
+ abstract getEntry(index: AbsoluteEntryIndex): Promise<T>;
+ abstract getTraceType(): TraceType;
}
diff --git a/tools/winscope/src/parsers/perfetto/parser_factory.ts b/tools/winscope/src/parsers/perfetto/parser_factory.ts
index 5a1568b..350aadd 100644
--- a/tools/winscope/src/parsers/perfetto/parser_factory.ts
+++ b/tools/winscope/src/parsers/perfetto/parser_factory.ts
@@ -15,7 +15,7 @@
*/
import {globalConfig} from 'common/global_config';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {UrlUtils} from 'common/url_utils';
import {ProgressListener} from 'messaging/progress_listener';
import {UserNotificationsListener} from 'messaging/user_notifications_listener';
@@ -44,7 +44,7 @@
async createParsers(
traceFile: TraceFile,
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
progressListener?: ProgressListener,
notificationListener?: UserNotificationsListener,
): Promise<Array<Parser<object>>> {
@@ -85,7 +85,7 @@
const parser = new ParserType(
traceFile,
traceProcessor,
- timestampFactory,
+ timestampConverter,
);
await parser.parse();
parsers.push(parser);
diff --git a/tools/winscope/src/parsers/protolog/legacy/parser_protolog.ts b/tools/winscope/src/parsers/protolog/legacy/parser_protolog.ts
index 8567947..39516cf 100644
--- a/tools/winscope/src/parsers/protolog/legacy/parser_protolog.ts
+++ b/tools/winscope/src/parsers/protolog/legacy/parser_protolog.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {LogMessage} from 'parsers/protolog/log_message';
import {ParserProtologUtils} from 'parsers/protolog/parser_protolog_utils';
@@ -36,7 +36,7 @@
private static readonly PROTOLOG_32_BIT_VERSION = '1.0.0';
private static readonly PROTOLOG_64_BIT_VERSION = '2.0.0';
- private realToElapsedTimeOffsetNs: bigint | undefined;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.PROTO_LOG;
@@ -46,6 +46,14 @@
return ParserProtoLog.MAGIC_NUMBER;
}
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): com.android.internal.protolog.IProtoLogMessage[] {
@@ -71,7 +79,7 @@
throw new TypeError(message);
}
- this.realToElapsedTimeOffsetNs =
+ this.realToBootTimeOffsetNs =
BigInt(
assertDefined(fileProto.realTimeToElapsedTimeOffsetMillis).toString(),
) * 1000000n;
@@ -92,31 +100,16 @@
return fileProto.log;
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: com.android.internal.protolog.IProtoLogMessage,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: com.android.internal.protolog.IProtoLogMessage,
): PropertyTreeNode {
let messageHash = assertDefined(entry.messageHash).toString();
@@ -136,9 +129,8 @@
const logMessage = this.makeLogMessage(entry, message, tag);
return ParserProtologUtils.makeMessagePropertiesTree(
logMessage,
- timestampType,
- this.realToElapsedTimeOffsetNs,
- this.timestampFactory,
+ this.timestampConverter,
+ this.getRealToMonotonicTimeOffsetNs() !== undefined,
);
}
diff --git a/tools/winscope/src/parsers/protolog/legacy/parser_protolog_test.ts b/tools/winscope/src/parsers/protolog/legacy/parser_protolog_test.ts
index 418affa..5d3f2aa 100644
--- a/tools/winscope/src/parsers/protolog/legacy/parser_protolog_test.ts
+++ b/tools/winscope/src/parsers/protolog/legacy/parser_protolog_test.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -25,7 +25,7 @@
interface ExpectedMessage {
'message': string;
- 'ts': {'real': string; 'elapsed': string};
+ 'ts': string;
'at': string;
'level': string;
'tag': string;
@@ -36,8 +36,6 @@
traceFile: string,
timestampCount: number,
first3ExpectedRealTimestamps: Timestamp[],
- first3ExpectedElapsedTimestamps: Timestamp[],
- first3WithTimezoneTimestamps: Timestamp[],
expectedFirstMessage: ExpectedMessage,
) =>
() => {
@@ -62,51 +60,22 @@
expect(parser.getLengthEntries()).toEqual(timestampCount);
});
- it('provides elapsed timestamps', () => {
- const timestamps = parser.getTimestamps(TimestampType.ELAPSED)!;
- expect(timestamps.length).toEqual(timestampCount);
-
- expect(timestamps.slice(0, 3)).toEqual(first3ExpectedElapsedTimestamps);
- });
-
- it('provides real timestamps', () => {
- const timestamps = parser.getTimestamps(TimestampType.REAL)!;
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(timestampCount);
expect(timestamps.slice(0, 3)).toEqual(first3ExpectedRealTimestamps);
});
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- traceFile,
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.PROTO_LOG,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(first3ExpectedElapsedTimestamps);
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- ).slice(0, 3),
- ).toEqual(first3WithTimezoneTimestamps);
- });
-
- it('reconstructs human-readable log message (ELAPSED time)', async () => {
- const message = await parser.getEntry(0, TimestampType.ELAPSED);
+ it('reconstructs human-readable log message', async () => {
+ const message = await parser.getEntry(0);
expect(
assertDefined(message.getChildByName('text')).formattedValue(),
).toEqual(expectedFirstMessage['message']);
expect(
assertDefined(message.getChildByName('timestamp')).formattedValue(),
- ).toEqual(expectedFirstMessage['ts']['elapsed']);
+ ).toEqual(expectedFirstMessage['ts']);
expect(
assertDefined(message.getChildByName('tag')).formattedValue(),
).toEqual(expectedFirstMessage['tag']);
@@ -119,14 +88,14 @@
});
it('reconstructs human-readable log message (REAL time)', async () => {
- const message = await parser.getEntry(0, TimestampType.REAL);
+ const message = await parser.getEntry(0);
expect(
assertDefined(message.getChildByName('text')).formattedValue(),
).toEqual(expectedFirstMessage['message']);
expect(
assertDefined(message.getChildByName('timestamp')).formattedValue(),
- ).toEqual(expectedFirstMessage['ts']['real']);
+ ).toEqual(expectedFirstMessage['ts']);
expect(
assertDefined(message.getChildByName('tag')).formattedValue(),
).toEqual(expectedFirstMessage['tag']);
@@ -146,27 +115,14 @@
'traces/elapsed_and_real_timestamp/ProtoLog32.pb',
50,
[
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655727125377266486n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655727125377336718n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655727125377350430n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850746266486n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850746336718n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850746350430n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655746925377266486n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655746925377336718n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1655746925377350430n),
+ TimestampConverterUtils.makeRealTimestamp(1655727125377266486n),
+ TimestampConverterUtils.makeRealTimestamp(1655727125377336718n),
+ TimestampConverterUtils.makeRealTimestamp(1655727125377350430n),
],
{
'message':
'InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false',
- 'ts': {
- 'real': '2022-06-20T12:12:05.377266486',
- 'elapsed': '14m10s746ms266486ns',
- },
+ 'ts': '2022-06-20T12:12:05.377266486',
'tag': 'WindowManager',
'level': 'DEBUG',
'at': 'com/android/server/wm/InsetsSourceProvider.java',
@@ -179,26 +135,13 @@
'traces/elapsed_and_real_timestamp/ProtoLog64.pb',
4615,
[
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709196806399529939n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709196806399763866n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709196806400297151n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1315553529939n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1315553763866n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1315554297151n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709216606399529939n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709216606399763866n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1709216606400297151n),
+ TimestampConverterUtils.makeRealTimestamp(1709196806399529939n),
+ TimestampConverterUtils.makeRealTimestamp(1709196806399763866n),
+ TimestampConverterUtils.makeRealTimestamp(1709196806400297151n),
],
{
'message': 'Starting activity when config will change = false',
- 'ts': {
- 'real': '2024-02-29T08:53:26.399529939',
- 'elapsed': '21m55s553ms529939ns',
- },
+ 'ts': '2024-02-29T08:53:26.399529939',
'tag': 'WindowManager',
'level': 'VERBOSE',
'at': 'com/android/server/wm/ActivityStarter.java',
@@ -211,26 +154,13 @@
'traces/elapsed_and_real_timestamp/ProtoLogMissingConfigMessage.pb',
7295,
[
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669053909777144978n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669053909778011697n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669053909778800707n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(24398190144978n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(24398191011697n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(24398191800707n),
- ],
- [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669073709777144978n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669073709778011697n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1669073709778800707n),
+ TimestampConverterUtils.makeRealTimestamp(1669053909777144978n),
+ TimestampConverterUtils.makeRealTimestamp(1669053909778011697n),
+ TimestampConverterUtils.makeRealTimestamp(1669053909778800707n),
],
{
'message': 'SURFACE isColorSpaceAgnostic=true: NotificationShade',
- 'ts': {
- 'real': '2022-11-21T18:05:09.777144978',
- 'elapsed': '6h46m38s190ms144978ns',
- },
+ 'ts': '2022-11-21T18:05:09.777144978',
'tag': 'WindowManager',
'level': 'INFO',
'at': 'com/android/server/wm/WindowSurfaceController.java',
diff --git a/tools/winscope/src/parsers/protolog/parser_protolog_utils.ts b/tools/winscope/src/parsers/protolog/parser_protolog_utils.ts
index 5fcc3b6..9d126b6 100644
--- a/tools/winscope/src/parsers/protolog/parser_protolog_utils.ts
+++ b/tools/winscope/src/parsers/protolog/parser_protolog_utils.ts
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-import {TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {SetFormatters} from 'parsers/operations/set_formatters';
import {
MakeTimestampStrategyType,
TransformToTimestamp,
} from 'parsers/operations/transform_to_timestamp';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {LogMessage} from './log_message';
@@ -29,9 +28,8 @@
export class ParserProtologUtils {
static makeMessagePropertiesTree(
logMessage: LogMessage,
- timestampType: TimestampType,
- realToElapsedTimeOffsetNs: bigint | undefined,
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
+ isMonotonic: boolean,
): PropertyTreeNode {
const tree = new PropertyTreeBuilderFromProto()
.setData(logMessage)
@@ -40,19 +38,14 @@
.setVisitPrototype(false)
.build();
- const customFormatters = new Map([['timestamp', TIMESTAMP_FORMATTER]]);
+ const customFormatters = new Map([['timestamp', TIMESTAMP_NODE_FORMATTER]]);
- let strategy: MakeTimestampStrategyType | undefined;
- if (timestampType === TimestampType.REAL) {
- strategy = (valueNs: bigint) => {
- return timestampFactory.makeRealTimestamp(
- valueNs,
- realToElapsedTimeOffsetNs,
- );
- };
- } else {
- strategy = timestampFactory.makeElapsedTimestamp;
- }
+ const strategy: MakeTimestampStrategyType = (valueNs: bigint) => {
+ if (isMonotonic) {
+ return timestampConverter.makeTimestampFromMonotonicNs(valueNs);
+ }
+ return timestampConverter.makeTimestampFromBootTimeNs(valueNs);
+ };
new TransformToTimestamp(['timestamp'], strategy).apply(tree);
new SetFormatters(undefined, customFormatters).apply(tree);
diff --git a/tools/winscope/src/parsers/protolog/perfetto/parser_protolog.ts b/tools/winscope/src/parsers/protolog/perfetto/parser_protolog.ts
index a84f549..9ce2138 100644
--- a/tools/winscope/src/parsers/protolog/perfetto/parser_protolog.ts
+++ b/tools/winscope/src/parsers/protolog/perfetto/parser_protolog.ts
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-import {TimestampType} from 'common/time';
import {AbstractParser} from 'parsers/perfetto/abstract_parser';
import {LogMessage} from 'parsers/protolog/log_message';
import {ParserProtologUtils} from 'parsers/protolog/parser_protolog_utils';
@@ -41,11 +40,8 @@
return TraceType.PROTO_LOG;
}
- override async getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<PropertyTreeNode> {
- const protologEntry = await this.queryProtoLogEntry(index);
+ override async getEntry(index: number): Promise<PropertyTreeNode> {
+ const protologEntry = await this.queryEntry(index);
const logMessage: LogMessage = {
text: protologEntry.message,
tag: protologEntry.tag,
@@ -56,9 +52,8 @@
return ParserProtologUtils.makeMessagePropertiesTree(
logMessage,
- timestampType,
- this.realToElapsedTimeOffsetNs,
- this.timestampFactory,
+ this.timestampConverter,
+ this.getRealToMonotonicTimeOffsetNs() !== undefined,
);
}
@@ -66,7 +61,7 @@
return 'protolog';
}
- private async queryProtoLogEntry(
+ protected override async queryEntry(
index: number,
): Promise<PerfettoLogMessageTableRow> {
const sql = `
diff --git a/tools/winscope/src/parsers/protolog/perfetto/parser_protolog_test.ts b/tools/winscope/src/parsers/protolog/perfetto/parser_protolog_test.ts
index a899b7f..cd10ad7 100644
--- a/tools/winscope/src/parsers/protolog/perfetto/parser_protolog_test.ts
+++ b/tools/winscope/src/parsers/protolog/perfetto/parser_protolog_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {TraceType} from 'trace/trace_type';
@@ -36,58 +35,22 @@
expect(parser.getTraceType()).toEqual(TraceType.PROTO_LOG);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(75);
// TODO: They shouldn't all have the same timestamp...
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(5939002349294n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(5939002349294n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(5939002349294n),
+ TimestampConverterUtils.makeRealTimestamp(1706547264827624563n),
+ TimestampConverterUtils.makeRealTimestamp(1706547264827624563n),
+ TimestampConverterUtils.makeRealTimestamp(1706547264827624563n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
-
- expect(timestamps.length).toEqual(75);
-
- // TODO: They shouldn't all have the same timestamp...
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1706547264827624563n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1706547264827624563n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1706547264827624563n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
-
- it('reconstructs human-readable log message (ELAPSED time)', async () => {
- const message = await parser.getEntry(0, TimestampType.ELAPSED);
-
- expect(
- assertDefined(message.getChildByName('text')).formattedValue(),
- ).toEqual('Sent Transition (#11) createdAt=01-29 17:54:23.793');
- expect(
- assertDefined(message.getChildByName('timestamp')).formattedValue(),
- ).toEqual('1h38m59s2ms349294ns');
- expect(
- assertDefined(message.getChildByName('tag')).formattedValue(),
- ).toEqual('WindowManager');
- expect(
- assertDefined(message.getChildByName('level')).formattedValue(),
- ).toEqual('VERBOSE');
- expect(
- assertDefined(message.getChildByName('at')).formattedValue(),
- ).toEqual('<NO_LOC>');
- });
-
it('reconstructs human-readable log message (REAL time)', async () => {
- const message = await parser.getEntry(0, TimestampType.REAL);
+ const message = await parser.getEntry(0);
expect(
assertDefined(message.getChildByName('text')).formattedValue(),
@@ -105,26 +68,4 @@
assertDefined(message.getChildByName('at')).formattedValue(),
).toEqual('<NO_LOC>');
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = await UnitTestUtils.getPerfettoParser(
- TraceType.PROTO_LOG,
- 'traces/perfetto/protolog.perfetto-trace',
- true,
- );
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(TraceType.PROTO_LOG);
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(5939002349294n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1706567064827624563n),
- );
- });
});
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screen_recording.ts b/tools/winscope/src/parsers/screen_recording/parser_screen_recording.ts
index a10a4a8..89c0b73 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screen_recording.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screen_recording.ts
@@ -15,22 +15,16 @@
*/
import {ArrayUtils} from 'common/array_utils';
-import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {CoarseVersion} from 'trace/coarse_version';
import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
import {ScreenRecordingUtils} from 'trace/screen_recording_utils';
import {TraceType} from 'trace/trace_type';
-class ScreenRecordingMetadataEntry {
- constructor(
- public timestampElapsedNs: bigint,
- public timestampRealtimeNs: bigint,
- ) {}
-}
-
class ParserScreenRecording extends AbstractParser {
+ private realToBootTimeOffsetNs: bigint | undefined;
+
override getTraceType(): TraceType {
return TraceType.SCREEN_RECORDING;
}
@@ -40,10 +34,18 @@
}
override getMagicNumber(): number[] {
- return ParserScreenRecording.MPEG4_MAGIC_NMBER;
+ return ParserScreenRecording.MPEG4_MAGIC_NUMBER;
}
- override decodeTrace(videoData: Uint8Array): ScreenRecordingMetadataEntry[] {
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override decodeTrace(videoData: Uint8Array): Array<bigint> {
const posVersion = this.searchMagicString(videoData);
const [posTimeOffset, metadataVersion] = this.parseMetadataVersion(
videoData,
@@ -68,10 +70,11 @@
other traces. Metadata contains monotonic time instead of elapsed.`);
}
- const [posCount, timeOffsetNs] = this.parseRealToElapsedTimeOffsetNs(
+ const [posCount, timeOffsetNs] = this.parserealToBootTimeOffsetNs(
videoData,
posTimeOffset,
);
+ this.realToBootTimeOffsetNs = timeOffsetNs;
const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
const timestampsElapsedNs = this.parseTimestampsElapsedNs(
videoData,
@@ -79,47 +82,20 @@
count,
);
- return timestampsElapsedNs.map((timestampElapsedNs: bigint) => {
- return new ScreenRecordingMetadataEntry(
- timestampElapsedNs,
- timestampElapsedNs + timeOffsetNs,
- );
- });
+ return timestampsElapsedNs;
}
- override getTimestamp(
- type: TimestampType,
- decodedEntry: ScreenRecordingMetadataEntry,
- ): undefined | Timestamp {
- if (type !== TimestampType.ELAPSED && type !== TimestampType.REAL) {
- return undefined;
- }
- if (type === TimestampType.ELAPSED) {
- return this.timestampFactory.makeElapsedTimestamp(
- decodedEntry.timestampElapsedNs,
- );
- } else if (type === TimestampType.REAL) {
- return this.timestampFactory.makeRealTimestamp(
- decodedEntry.timestampRealtimeNs,
- );
- }
- return undefined;
+ protected override getTimestamp(decodedEntry: bigint): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(decodedEntry);
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
- entry: ScreenRecordingMetadataEntry,
+ entry: bigint,
): ScreenRecordingTraceEntry {
- const initialTimestamp = assertDefined(
- this.getTimestamps(TimestampType.ELAPSED),
- )[0];
- const currentTimestamp = this.timestampFactory.makeElapsedTimestamp(
- entry.timestampElapsedNs,
- );
const videoTimeSeconds = ScreenRecordingUtils.timestampToVideoTimeSeconds(
- initialTimestamp,
- currentTimestamp,
+ this.decodedEntries[0],
+ entry,
);
const videoData = this.traceFile.file;
return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
@@ -153,7 +129,7 @@
return [pos, version];
}
- private parseRealToElapsedTimeOffsetNs(
+ private parserealToBootTimeOffsetNs(
videoData: Uint8Array,
pos: number,
): [number, bigint] {
@@ -202,7 +178,7 @@
return timestamps;
}
- private static readonly MPEG4_MAGIC_NMBER = [
+ private static readonly MPEG4_MAGIC_NUMBER = [
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32,
]; // ....ftypmp42
private static readonly WINSCOPE_META_MAGIC_STRING = [
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy.ts b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy.ts
index 1def2a9..f58c567 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy.ts
@@ -15,10 +15,10 @@
*/
import {ArrayUtils} from 'common/array_utils';
-import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
+import {ScreenRecordingUtils} from 'trace/screen_recording_utils';
import {TraceType} from 'trace/trace_type';
class ParserScreenRecordingLegacy extends AbstractParser {
@@ -27,38 +27,35 @@
}
override getMagicNumber(): number[] {
- return ParserScreenRecordingLegacy.MPEG4_MAGIC_NMBER;
+ return ParserScreenRecordingLegacy.MPEG4_MAGIC_NUMBER;
}
- override decodeTrace(videoData: Uint8Array): Timestamp[] {
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override decodeTrace(videoData: Uint8Array): Array<bigint> {
const posCount = this.searchMagicString(videoData);
const [posTimestamps, count] = this.parseFramesCount(videoData, posCount);
- return this.parseTimestamps(videoData, posTimestamps, count);
+ return this.parseVideoData(videoData, posTimestamps, count);
}
- override getTimestamp(
- type: TimestampType,
- decodedEntry: Timestamp,
- ): undefined | Timestamp {
- if (type !== TimestampType.ELAPSED) {
- return undefined;
- }
- return decodedEntry;
+ protected override getTimestamp(decodedEntry: bigint): Timestamp {
+ return this.timestampConverter.makeTimestampFromMonotonicNs(decodedEntry);
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
- entry: Timestamp,
+ entry: bigint,
): ScreenRecordingTraceEntry {
- const currentTimestamp = entry;
- const initialTimestamp = assertDefined(
- this.getTimestamps(TimestampType.ELAPSED),
- )[0];
- const videoTimeSeconds =
- Number(currentTimestamp.getValueNs() - initialTimestamp.getValueNs()) /
- 1000000000 +
- ParserScreenRecordingLegacy.EPSILON;
+ const videoTimeSeconds = ScreenRecordingUtils.timestampToVideoTimeSeconds(
+ this.decodedEntries[0],
+ entry,
+ );
const videoData = this.traceFile.file;
return new ScreenRecordingTraceEntry(videoTimeSeconds, videoData);
}
@@ -91,27 +88,27 @@
return [pos, framesCount];
}
- private parseTimestamps(
+ private parseVideoData(
videoData: Uint8Array,
pos: number,
count: number,
- ): Timestamp[] {
+ ): Array<bigint> {
if (pos + count * 8 > videoData.length) {
throw new TypeError(
'Failed to parse timestamps. Video data is too short.',
);
}
- const timestamps: Timestamp[] = [];
+ const timestamps: Array<bigint> = [];
for (let i = 0; i < count; ++i) {
const value =
ArrayUtils.toUintLittleEndian(videoData, pos, pos + 8) * 1000n;
pos += 8;
- timestamps.push(this.timestampFactory.makeElapsedTimestamp(value));
+ timestamps.push(value);
}
return timestamps;
}
- private static readonly MPEG4_MAGIC_NMBER = [
+ private static readonly MPEG4_MAGIC_NUMBER = [
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32,
]; // ....ftypmp42
private static readonly WINSCOPE_META_MAGIC_STRING = [
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy_test.ts b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy_test.ts
index b3c32f9..16cf41d 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy_test.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_legacy_test.ts
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -39,66 +39,36 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(85);
let expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446131807000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446158500000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446167117000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19446131807000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19446158500000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19446167117000n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19448470076000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19448487525000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19448501007000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19448470076000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19448487525000n),
+ TimestampConverterUtils.makeElapsedTimestamp(19448501007000n),
];
expect(timestamps.slice(timestamps.length - 3, timestamps.length)).toEqual(
expected,
);
});
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
- });
-
- it('does not apply timezone info', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/screen_recording.mp4',
- true,
- )) as Parser<ScreenRecordingTraceEntry>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SCREEN_RECORDING,
- );
-
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446131807000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446158500000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(19446167117000n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(expectedElapsed);
- });
-
it('retrieves trace entry', async () => {
{
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
}
{
- const entry = await parser.getEntry(
- parser.getLengthEntries() - 1,
- TimestampType.ELAPSED,
- );
+ const entry = await parser.getEntry(parser.getLengthEntries() - 1);
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001);
}
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_test.ts b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_test.ts
index 8688488..ff9c279 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screen_recording_test.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screen_recording_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -40,77 +39,27 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(123);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211827840430n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211842401430n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211862172430n),
+ TimestampConverterUtils.makeRealTimestamp(1666361048792787045n),
+ TimestampConverterUtils.makeRealTimestamp(1666361048807348045n),
+ TimestampConverterUtils.makeRealTimestamp(1666361048827119045n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
-
- expect(timestamps.length).toEqual(123);
-
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666361048792787045n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666361048807348045n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666361048827119045n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/screen_recording_metadata_v2.mp4',
- true,
- )) as Parser<ScreenRecordingTraceEntry>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SCREEN_RECORDING,
- );
-
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211827840430n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211842401430n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(211862172430n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(expectedElapsed);
-
- const expectedReal = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666380848792787045n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666380848807348045n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1666380848827119045n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- ).slice(0, 3),
- ).toEqual(expectedReal);
- });
-
it('retrieves trace entry', async () => {
{
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
}
{
- const entry = await parser.getEntry(
- parser.getLengthEntries() - 1,
- TimestampType.REAL,
- );
+ const entry = await parser.getEntry(parser.getLengthEntries() - 1);
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(1.371077, 0.001);
}
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screenshot.ts b/tools/winscope/src/parsers/screen_recording/parser_screenshot.ts
index 194c824..06dfc9b 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screenshot.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screenshot.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {CoarseVersion} from 'trace/coarse_version';
import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
@@ -38,23 +37,24 @@
return ParserScreenshot.MAGIC_NUMBER;
}
- override getTimestamp(
- type: TimestampType,
- decodedEntry: number,
- ): Timestamp | undefined {
- if (NO_TIMEZONE_OFFSET_FACTORY.canMakeTimestampFromType(type, 0n)) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(type, 0n, 0n);
- }
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
return undefined;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ protected override getTimestamp(decodedEntry: number): Timestamp {
+ return this.timestampConverter.makeZeroTimestamp();
+ }
+
override decodeTrace(screenshotData: Uint8Array): number[] {
return [0]; // require a non-empty array to be returned so trace can provide timestamps
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: number,
): ScreenRecordingTraceEntry {
const screenshotData = this.traceFile.file;
diff --git a/tools/winscope/src/parsers/screen_recording/parser_screenshot_test.ts b/tools/winscope/src/parsers/screen_recording/parser_screenshot_test.ts
index 1172456..e8bccd8 100644
--- a/tools/winscope/src/parsers/screen_recording/parser_screenshot_test.ts
+++ b/tools/winscope/src/parsers/screen_recording/parser_screenshot_test.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverter} from 'common/timestamp_converter';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {ScreenRecordingTraceEntry} from 'trace/screen_recording';
@@ -29,12 +29,14 @@
let file: File;
beforeAll(async () => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
file = await UnitTestUtils.getFixtureFile('traces/screenshot.png');
parser = new ParserScreenshot(
new TraceFile(file),
- NO_TIMEZONE_OFFSET_FACTORY,
+ new TimestampConverter(TimestampConverterUtils.UTC_TIMEZONE_INFO, 0n),
);
await parser.parse();
+ parser.createTimestamps();
});
it('has expected trace type', () => {
@@ -45,42 +47,28 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
- const expected = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n);
- timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
- });
-
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
-
- const expected = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
+ const expected = TimestampConverterUtils.makeElapsedTimestamp(0n);
timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
});
it('does not apply timezone info', async () => {
const parserWithTimezoneInfo = new ParserScreenshot(
new TraceFile(file),
- UnitTestUtils.TIMESTAMP_FACTORY_WITH_TIMEZONE,
+ TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET,
);
await parserWithTimezoneInfo.parse();
- const expectedElapsed = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n);
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED)).forEach(
- (timestamp) => expect(timestamp).toEqual(expectedElapsed),
- );
-
- const expectedReal = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
- assertDefined(parser.getTimestamps(TimestampType.REAL)).forEach(
- (timestamp) => expect(timestamp).toEqual(expectedReal),
+ const expectedReal = TimestampConverterUtils.makeElapsedTimestamp(0n);
+ assertDefined(parser.getTimestamps()).forEach((timestamp) =>
+ expect(timestamp).toEqual(expectedReal),
);
});
it('retrieves entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(entry.isImage).toBeTrue();
});
diff --git a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger.ts b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger.ts
index 8ad7291..068a252 100644
--- a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger.ts
+++ b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger.ts
@@ -15,8 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
@@ -94,7 +93,8 @@
),
};
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToMonotonicTimeOffsetNs: bigint | undefined;
+ private isDump = false;
override getTraceType(): TraceType {
return TraceType.SURFACE_FLINGER;
@@ -104,6 +104,14 @@
return ParserSurfaceFlinger.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return this.realToMonotonicTimeOffsetNs;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): android.surfaceflinger.ILayersTraceProto[] {
@@ -113,52 +121,30 @@
const timeOffset = BigInt(
decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToMonotonicTimeOffsetNs =
+ timeOffset !== 0n ? timeOffset : undefined;
+ this.isDump =
+ decoded.entry?.length === 1 &&
+ !Object.prototype.hasOwnProperty.call(
+ decoded.entry[0],
+ 'elapsedRealtimeNanos',
+ );
return decoded.entry ?? [];
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: android.surfaceflinger.ILayersTraceProto,
- ): undefined | Timestamp {
- const isDump = !Object.prototype.hasOwnProperty.call(
- entry,
- 'elapsedRealtimeNanos',
+ ): Timestamp {
+ if (this.isDump) {
+ return this.timestampConverter.makeZeroTimestamp();
+ }
+ return this.timestampConverter.makeTimestampFromMonotonicNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- isDump &&
- NO_TIMEZONE_OFFSET_FACTORY.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(type, 0n, 0n);
- }
-
- if (!isDump) {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
- );
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- }
-
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: android.surfaceflinger.ILayersTraceProto,
): HierarchyTreeNode {
return this.makeHierarchyTree(entry);
diff --git a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_dump_test.ts b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_dump_test.ts
index 72f37c6..0b62e05 100644
--- a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_dump_test.ts
+++ b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_dump_test.ts
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -22,7 +22,11 @@
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
describe('ParserSurfaceFlingerDump', () => {
- describe('trace with elapsed + real timestamp', () => {
+ beforeAll(() => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
+ });
+
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -39,42 +43,28 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamp', () => {
- const expected = [NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n)];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
- });
-
- it('provides real timestamp (always zero)', () => {
- const expected = [NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n)];
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ it('provides timestamps (always zero)', () => {
+ const expected = [TimestampConverterUtils.makeElapsedTimestamp(0n)];
+ expect(parser.getTimestamps()).toEqual(expected);
});
it('does not apply timezone info', async () => {
const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/dump_SurfaceFlinger.pb',
- true,
+ UnitTestUtils.getTimestampConverter(true),
)) as Parser<HierarchyTreeNode>;
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- ];
- expect(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).toEqual(expectedElapsed);
-
- const expectedReal = [NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n)];
- expect(parserWithTimezoneInfo.getTimestamps(TimestampType.REAL)).toEqual(
- expectedReal,
- );
+ const expected = [TimestampConverterUtils.makeElapsedTimestamp(0n)];
+ expect(parserWithTimezoneInfo.getTimestamps()).toEqual(expected);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(0);
expect(entry).toBeTruthy();
});
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -91,13 +81,9 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamp (always zero)', () => {
- const expected = [NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n)];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
- });
-
- it("doesn't provide real timestamp", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
+ it('provides timestamp (always zero)', () => {
+ const expected = [TimestampConverterUtils.makeElapsedTimestamp(0n)];
+ expect(parser.getTimestamps()).toEqual(expected);
});
});
});
diff --git a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_test.ts b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_test.ts
index 79515ae..d925ad7 100644
--- a/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_test.ts
+++ b/tools/winscope/src/parsers/surface_flinger/legacy/parser_surface_flinger_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -27,7 +26,7 @@
import {UiTreeUtils} from 'viewers/common/ui_tree_utils';
describe('ParserSurfaceFlinger', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
let trace: Trace<HierarchyTreeNode>;
@@ -50,68 +49,25 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14500282843n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14631249355n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15403446377n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089102062832n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089233029344n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090005226366n),
];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089102062832n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089233029344n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090005226366n),
- ];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.REAL)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/SurfaceFlinger.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SURFACE_FLINGER,
+ expect(assertDefined(parser.getTimestamps()).slice(0, 3)).toEqual(
+ expected,
);
-
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14500282843n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14631249355n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15403446377n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(expectedElapsed);
-
- const expectedReal = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889102062832n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889233029344n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126890005226366n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- ).slice(0, 3),
- ).toEqual(expectedReal);
});
it('provides correct root entry node', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ const entry = await parser.getEntry(1);
expect(entry.id).toEqual('LayerTraceEntry root');
expect(entry.name).toEqual('root');
});
it('decodes layer state flags', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
{
const layer = assertDefined(
entry.findDfs(UiTreeUtils.makeIdMatchFilter('27 Leaf:24:25#27')),
@@ -184,7 +140,7 @@
const parser = (await UnitTestUtils.getParser(
'traces/elapsed_and_real_timestamp/SurfaceFlinger_with_duplicated_ids.pb',
)) as Parser<HierarchyTreeNode>;
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeTruthy();
const layer = assertDefined(
@@ -214,7 +170,7 @@
});
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -231,34 +187,14 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED))[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850335483446n));
- });
-
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
- });
-
- it('does not apply timezone info', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/SurfaceFlinger.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SURFACE_FLINGER,
+ it('provides timestamps', () => {
+ expect(assertDefined(parser.getTimestamps())[0]).toEqual(
+ TimestampConverterUtils.makeElapsedTimestamp(850335483446n),
);
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850335483446n));
});
it('provides correct root entry node', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(0);
expect(entry.id).toEqual('LayerTraceEntry root');
expect(entry.name).toEqual('root');
});
diff --git a/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger.ts b/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger.ts
index 86a8eb9..22d032c 100644
--- a/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger.ts
+++ b/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger.ts
@@ -15,13 +15,12 @@
*/
import {assertDefined, assertTrue} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
import {TranslateIntDef} from 'parsers/operations/translate_intdef';
import {AbstractParser} from 'parsers/perfetto/abstract_parser';
-import {FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder';
+import {FakeProto, FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder';
import {FakeProtoTransformer} from 'parsers/perfetto/fake_proto_transformer';
import {Utils} from 'parsers/perfetto/utils';
import {RectsComputation} from 'parsers/surface_flinger/computations/rects_computation';
@@ -102,9 +101,9 @@
constructor(
traceFile: TraceFile,
traceProcessor: WasmEngineProxy,
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
) {
- super(traceFile, traceProcessor, timestampFactory);
+ super(traceFile, traceProcessor, timestampConverter);
this.layersSnapshotProtoTransformer = new FakeProtoTransformer(
assertDefined(ParserSurfaceFlinger.entryField.tamperedMessageType),
);
@@ -117,17 +116,8 @@
return TraceType.SURFACE_FLINGER;
}
- override async getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<HierarchyTreeNode> {
- let snapshotProto = await Utils.queryEntry(
- this.traceProcessor,
- this.getTableName(),
- index,
- );
- snapshotProto =
- this.layersSnapshotProtoTransformer.transform(snapshotProto);
+ override async getEntry(index: number): Promise<HierarchyTreeNode> {
+ const snapshotProto = await this.queryEntry(index);
const layerProtos = (await this.querySnapshotLayers(index)).map(
(layerProto) => this.layerProtoTransformer.transform(layerProto),
);
@@ -181,6 +171,15 @@
return 'surfaceflinger_layers_snapshot';
}
+ protected override async queryEntry(index: number): Promise<FakeProto> {
+ const snapshotProto = await Utils.queryEntry(
+ this.traceProcessor,
+ this.getTableName(),
+ index,
+ );
+ return this.layersSnapshotProtoTransformer.transform(snapshotProto);
+ }
+
private makeHierarchyTree(
snapshotProto: perfetto.protos.ILayersSnapshotProto,
layerProtos: perfetto.protos.ILayerProto[],
diff --git a/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger_test.ts b/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger_test.ts
index 3dc5a7c..a93fafb 100644
--- a/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger_test.ts
+++ b/tools/winscope/src/parsers/surface_flinger/perfetto/parser_surface_flinger_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -51,62 +50,24 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14500282843n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14631249355n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15403446377n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089102062832n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089233029344n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090005226366n),
];
- const actual = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3);
+ const actual = assertDefined(parser.getTimestamps()).slice(0, 3);
expect(actual).toEqual(expected);
});
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089102062832n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089233029344n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090005226366n),
- ];
- const actual = assertDefined(
- parser.getTimestamps(TimestampType.REAL),
- ).slice(0, 3);
- expect(actual).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = await UnitTestUtils.getPerfettoParser(
- TraceType.SURFACE_FLINGER,
- 'traces/perfetto/layers_trace.perfetto-trace',
- true,
- );
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SURFACE_FLINGER,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14500282843n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889102062832n),
- );
- });
-
it('provides correct root entry node', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ const entry = await parser.getEntry(1);
expect(entry.id).toEqual('LayerTraceEntry root');
expect(entry.name).toEqual('root');
});
it('decodes layer state flags', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
{
const layer = assertDefined(
entry.findDfs(UiTreeUtils.makeIdMatchFilter('27 Leaf:24:25#27')),
@@ -182,7 +143,7 @@
TraceType.SURFACE_FLINGER,
'traces/perfetto/layers_trace_with_duplicated_ids.perfetto-trace',
);
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
expect(entry).toBeTruthy();
const layer = assertDefined(
diff --git a/tools/winscope/src/parsers/transactions/legacy/parser_transactions.ts b/tools/winscope/src/parsers/transactions/legacy/parser_transactions.ts
index f4132cc..85ec513 100644
--- a/tools/winscope/src/parsers/transactions/legacy/parser_transactions.ts
+++ b/tools/winscope/src/parsers/transactions/legacy/parser_transactions.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
@@ -51,7 +51,7 @@
new TranslateChanges(),
];
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToMonotonicTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.TRANSACTIONS;
@@ -61,6 +61,14 @@
return ParserTransactions.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return this.realToMonotonicTimeOffsetNs;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): android.surfaceflinger.proto.ITransactionTraceEntry[] {
@@ -71,36 +79,22 @@
const timeOffset = BigInt(
decodedProto.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToMonotonicTimeOffsetNs =
+ timeOffset !== 0n ? timeOffset : undefined;
return decodedProto.entry ?? [];
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entryProto: android.surfaceflinger.proto.ITransactionTraceEntry,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entryProto.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromMonotonicNs(
+ BigInt(assertDefined(entryProto.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entryProto: android.surfaceflinger.proto.ITransactionTraceEntry,
): PropertyTreeNode {
return this.makePropertiesTree(entryProto);
diff --git a/tools/winscope/src/parsers/transactions/legacy/parser_transactions_test.ts b/tools/winscope/src/parsers/transactions/legacy/parser_transactions_test.ts
index c8f2678..6eb1818 100644
--- a/tools/winscope/src/parsers/transactions/legacy/parser_transactions_test.ts
+++ b/tools/winscope/src/parsers/transactions/legacy/parser_transactions_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -25,7 +24,7 @@
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
describe('ParserTransactions', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
@@ -43,76 +42,27 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(712);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2450981445n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2517952515n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(4021151449n),
+ TimestampConverterUtils.makeRealTimestamp(1659507541051480997n),
+ TimestampConverterUtils.makeRealTimestamp(1659507541118452067n),
+ TimestampConverterUtils.makeRealTimestamp(1659507542621651001n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
- it('provides real timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.REAL),
- );
-
- expect(timestamps.length).toEqual(712);
-
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507541051480997n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507541118452067n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507542621651001n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/Transactions.pb',
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.TRANSACTIONS,
- );
-
- const expectedElapsed = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2450981445n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2517952515n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(4021151449n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3),
- ).toEqual(expectedElapsed);
-
- const expectedReal = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659527341051480997n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659527341118452067n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659527342621651001n),
- ];
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- ).slice(0, 3),
- ).toEqual(expectedReal);
- });
-
- it('retrieves trace entry from real timestamp', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ it('retrieves trace entry from timestamp', async () => {
+ const entry = await parser.getEntry(1);
expect(entry.id).toEqual('TransactionsTraceEntry entry');
});
it("decodes 'what' field in proto", async () => {
{
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
const transactions = assertDefined(
entry.getChildByName('transactions'),
);
@@ -136,7 +86,7 @@
).toEqual('eFlagsChanged | eDestinationFrameChanged');
}
{
- const entry = await parser.getEntry(222, TimestampType.REAL);
+ const entry = await parser.getEntry(222);
const transactions = assertDefined(
entry.getChildByName('transactions'),
);
@@ -167,7 +117,7 @@
});
});
- describe('trace with elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<PropertyTreeNode>;
beforeAll(async () => {
@@ -180,39 +130,17 @@
expect(parser.getTraceType()).toEqual(TraceType.TRANSACTIONS);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(4997);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14862317023n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14873423549n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14884850511n),
+ TimestampConverterUtils.makeElapsedTimestamp(14862317023n),
+ TimestampConverterUtils.makeElapsedTimestamp(14873423549n),
+ TimestampConverterUtils.makeElapsedTimestamp(14884850511n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
-
- it('does not apply timezone info', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/Transactions.pb',
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.TRANSACTIONS,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14862317023n));
- });
-
- it("doesn't provide real timestamps", () => {
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(undefined);
- });
});
});
diff --git a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
index fbed76d..90bc7f3 100644
--- a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
+++ b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions.ts
@@ -15,11 +15,11 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
import {AbstractParser} from 'parsers/perfetto/abstract_parser';
+import {FakeProto} from 'parsers/perfetto/fake_proto_builder';
import {FakeProtoTransformer} from 'parsers/perfetto/fake_proto_transformer';
import {Utils} from 'parsers/perfetto/utils';
import {TamperedMessageType} from 'parsers/tampered_message_type';
@@ -57,9 +57,9 @@
constructor(
traceFile: TraceFile,
traceProcessor: WasmEngineProxy,
- timestampFactory: TimestampFactory,
+ timestampConverter: ParserTimestampConverter,
) {
- super(traceFile, traceProcessor, timestampFactory);
+ super(traceFile, traceProcessor, timestampConverter);
this.protoTransformer = new FakeProtoTransformer(
assertDefined(
@@ -72,16 +72,17 @@
return TraceType.TRANSACTIONS;
}
- override async getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<PropertyTreeNode> {
- let entryProto = await Utils.queryEntry(
+ override async queryEntry(index: number): Promise<FakeProto> {
+ const entryProto = await Utils.queryEntry(
this.traceProcessor,
this.getTableName(),
index,
);
- entryProto = this.protoTransformer.transform(entryProto);
+ return this.protoTransformer.transform(entryProto);
+ }
+
+ override async getEntry(index: number): Promise<PropertyTreeNode> {
+ const entryProto = await this.queryEntry(index);
return this.makePropertiesTree(entryProto);
}
diff --git a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions_test.ts b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions_test.ts
index 0f124eb..858113f 100644
--- a/tools/winscope/src/parsers/transactions/perfetto/parser_transactions_test.ts
+++ b/tools/winscope/src/parsers/transactions/perfetto/parser_transactions_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -43,66 +42,27 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(712);
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2450981445n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2517952515n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(4021151449n),
+ TimestampConverterUtils.makeRealTimestamp(1659507541051480997n),
+ TimestampConverterUtils.makeRealTimestamp(1659507541118452067n),
+ TimestampConverterUtils.makeRealTimestamp(1659507542621651001n),
];
expect(timestamps.slice(0, 3)).toEqual(expected);
});
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
-
- expect(timestamps.length).toEqual(712);
-
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507541051480997n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507541118452067n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659507542621651001n),
- ];
- expect(timestamps.slice(0, 3)).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = await UnitTestUtils.getPerfettoParser(
- TraceType.TRANSACTIONS,
- 'traces/perfetto/transactions_trace.perfetto-trace',
- true,
- );
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.TRANSACTIONS,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(2450981445n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659527341051480997n),
- );
- });
-
- it('retrieves trace entry from real timestamp', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ it('retrieves trace entry from timestamp', async () => {
+ const entry = await parser.getEntry(1);
expect(entry.id).toEqual('TransactionsTraceEntry entry');
});
it('transforms fake proto built from trace processor args', async () => {
- const entry0 = await parser.getEntry(0, TimestampType.REAL);
- const entry2 = await parser.getEntry(2, TimestampType.REAL);
+ const entry0 = await parser.getEntry(0);
+ const entry2 = await parser.getEntry(2);
// Add empty arrays
expect(entry0.getChildByName('addedDisplays')?.getAllChildren()).toEqual(
@@ -162,7 +122,7 @@
it("decodes 'what' field in proto", async () => {
{
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
const transactions = assertDefined(entry.getChildByName('transactions'));
expect(
transactions
@@ -183,7 +143,7 @@
).toEqual('eFlagsChanged | eDestinationFrameChanged');
}
{
- const entry = await parser.getEntry(222, TimestampType.REAL);
+ const entry = await parser.getEntry(222);
const transactions = assertDefined(entry.getChildByName('transactions'));
expect(
diff --git a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell.ts b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell.ts
index 350230f..c3ac4a4 100644
--- a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {INVALID_TIME_NS, Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
import root from 'protos/transitions/udc/json';
@@ -28,13 +27,21 @@
'com.android.wm.shell.WmShellTransitionTraceProto',
);
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
private handlerMapping: undefined | {[key: number]: string};
override getTraceType(): TraceType {
return TraceType.SHELL_TRANSITION;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
traceBuffer: Uint8Array,
): com.android.wm.shell.ITransition[] {
@@ -46,7 +53,7 @@
const timeOffset = BigInt(
decodedProto.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
this.handlerMapping = {};
for (const mapping of decodedProto.handlerMappings ?? []) {
@@ -58,41 +65,20 @@
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entryProto: com.android.wm.shell.ITransition,
): PropertyTreeNode {
- return this.makePropertiesTree(timestampType, entryProto);
+ return this.makePropertiesTree(entryProto);
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: com.android.wm.shell.ITransition,
- ): undefined | Timestamp {
+ ): Timestamp {
// for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
- const decodedEntry = this.processDecodedEntry(0, type, entry);
- const dispatchTimestamp: Timestamp | undefined = decodedEntry
- .getChildByName('shellData')
- ?.getChildByName('dispatchTimeNs')
- ?.getValue();
-
- if (type === TimestampType.REAL) {
- if (dispatchTimestamp) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
- dispatchTimestamp.getValueNs(),
- );
- } else {
- return this.timestampFactory.makeRealTimestamp(
- this.realToElapsedTimeOffsetNs ?? 0n,
- );
- }
- }
-
- if (type === TimestampType.ELAPSED) {
- const timestampNs = dispatchTimestamp?.getValueNs() ?? 0n;
- return NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(timestampNs);
- }
-
- return undefined;
+ return entry.dispatchTimeNs
+ ? this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(entry.dispatchTimeNs.toString()),
+ )
+ : this.timestampConverter.makeTimestampFromBootTimeNs(INVALID_TIME_NS);
}
protected getMagicNumber(): number[] | undefined {
@@ -113,8 +99,8 @@
) {
throw new Error('Requires at least one non-null timestamp');
}
- if (this.realToElapsedTimeOffsetNs === undefined) {
- throw new Error('missing realToElapsedTimeOffsetNs');
+ if (this.realToBootTimeOffsetNs === undefined) {
+ throw new Error('missing realToBootTimeOffsetNs');
}
if (this.handlerMapping === undefined) {
throw new Error('Missing handler mapping');
@@ -122,17 +108,15 @@
}
private makePropertiesTree(
- timestampType: TimestampType,
entryProto: com.android.wm.shell.ITransition,
): PropertyTreeNode {
this.validateShellTransitionEntry(entryProto);
const shellEntryTree = ParserTransitionsUtils.makeShellPropertiesTree({
entry: entryProto,
- realToElapsedTimeOffsetNs: this.realToElapsedTimeOffsetNs,
- timestampType,
+ realToBootTimeOffsetNs: this.realToBootTimeOffsetNs,
handlerMapping: this.handlerMapping,
- timestampFactory: this.timestampFactory,
+ timestampConverter: this.timestampConverter,
});
const wmEntryTree = ParserTransitionsUtils.makeWmPropertiesTree();
diff --git a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell_test.ts b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell_test.ts
index ba566d9..fa76d31 100644
--- a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell_test.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_shell_test.ts
@@ -15,8 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -41,55 +40,16 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(57649649922341n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(57651299086892n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
+ TimestampConverterUtils.makeRealTimestamp(1683188477607285317n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
+ TimestampConverterUtils.makeRealTimestamp(1683188479256449868n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
];
expect(timestamps).toEqual(expected);
});
-
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683188477607285317n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683188479256449868n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- ];
- expect(timestamps).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/shell_transition_trace.pb',
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.SHELL_TRANSITION,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(57649649922341n));
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683208277607285317n),
- );
- });
});
diff --git a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm.ts b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm.ts
index 1e1eb21..77c4bd3 100644
--- a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
+import {INVALID_TIME_NS, Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
import root from 'protos/transitions/udc/json';
@@ -27,18 +27,25 @@
'com.android.server.wm.shell.TransitionTraceProto',
);
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.WM_TRANSITION;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entryProto: com.android.server.wm.shell.ITransition,
): PropertyTreeNode {
- return this.makePropertiesTree(timestampType, entryProto);
+ return this.makePropertiesTree(entryProto);
}
override decodeTrace(
@@ -51,7 +58,7 @@
const timeOffset = BigInt(
decodedProto.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
return decodedProto.transitions ?? [];
}
@@ -60,16 +67,11 @@
return [0x09, 0x54, 0x52, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TRNTRACE
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: com.android.server.wm.shell.ITransition,
- ): undefined | Timestamp {
- // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
- return this.timestampFactory.makeTimestampFromType(
- type,
- 0n,
- this.realToElapsedTimeOffsetNs,
- );
+ ): Timestamp {
+ // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else INVALID_TIME_NS
+ return this.timestampConverter.makeTimestampFromBootTimeNs(INVALID_TIME_NS);
}
private validateWmTransitionEntry(
@@ -86,13 +88,12 @@
) {
throw new Error('Requires at least one non-null timestamp');
}
- if (this.realToElapsedTimeOffsetNs === undefined) {
- throw new Error('missing realToElapsedTimeOffsetNs');
+ if (this.realToBootTimeOffsetNs === undefined) {
+ throw new Error('missing realToBootTimeOffsetNs');
}
}
private makePropertiesTree(
- timestampType: TimestampType,
entryProto: com.android.server.wm.shell.ITransition,
): PropertyTreeNode {
this.validateWmTransitionEntry(entryProto);
@@ -100,9 +101,8 @@
const shellEntryTree = ParserTransitionsUtils.makeShellPropertiesTree();
const wmEntryTree = ParserTransitionsUtils.makeWmPropertiesTree({
entry: entryProto,
- realToElapsedTimeOffsetNs: this.realToElapsedTimeOffsetNs,
- timestampType,
- timestampFactory: this.timestampFactory,
+ realToBootTimeOffsetNs: this.realToBootTimeOffsetNs,
+ timestampConverter: this.timestampConverter,
});
return ParserTransitionsUtils.makeTransitionPropertiesTree(
diff --git a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm_test.ts b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm_test.ts
index f97c400..7bdb84d 100644
--- a/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm_test.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/parser_transitions_wm_test.ts
@@ -15,8 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -40,43 +39,11 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
- expect(timestamps.length).toEqual(8);
- const expected = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n);
- timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
- });
-
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
expect(timestamps.length).toEqual(8);
const expected =
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827956652323n);
+ TimestampConverterUtils.makeRealTimestamp(1683130827956652323n);
timestamps.forEach((timestamp) => expect(timestamp).toEqual(expected));
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.WM_TRANSITION,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683150627956652323n),
- );
- });
});
diff --git a/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions.ts b/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions.ts
index cf3029b..edd0880 100644
--- a/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AbstractTracesParser} from 'parsers/legacy/abstract_traces_parser';
import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
import {Trace} from 'trace/trace';
@@ -30,8 +30,8 @@
private readonly descriptors: string[];
private decodedEntries: PropertyTreeNode[] | undefined;
- constructor(traces: Traces) {
- super();
+ constructor(traces: Traces, timestampConverter: ParserTimestampConverter) {
+ super(timestampConverter);
const wmTransitionTrace = traces.getTrace(TraceType.WM_TRANSITION);
const shellTransitionTrace = traces.getTrace(TraceType.SHELL_TRANSITION);
if (wmTransitionTrace && shellTransitionTrace) {
@@ -66,17 +66,14 @@
this.decodedEntries = this.compressEntries(allEntries);
- await this.parseTimestamps();
+ await this.createTimestamps();
}
override getLengthEntries(): number {
return assertDefined(this.decodedEntries).length;
}
- override getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<PropertyTreeNode> {
+ override getEntry(index: number): Promise<PropertyTreeNode> {
const entry = assertDefined(this.decodedEntries)[index];
return Promise.resolve(entry);
}
@@ -89,35 +86,31 @@
return TraceType.TRANSITION;
}
- override getTimestamp(
- type: TimestampType,
- decodedEntry: PropertyTreeNode,
- ): undefined | Timestamp {
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ protected override getTimestamp(decodedEntry: PropertyTreeNode): Timestamp {
// for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
const shellData = decodedEntry.getChildByName('shellData');
const dispatchTimestamp: Timestamp | undefined = shellData
?.getChildByName('dispatchTimeNs')
?.getValue();
- const realToElapsedTimeOffsetNs: bigint =
+ const realToBootTimeOffsetNs: bigint =
shellData
- ?.getChildByName('realToElapsedTimeOffsetTimestamp')
+ ?.getChildByName('realToBootTimeOffsetTimestamp')
?.getValue()
?.getValueNs() ?? 0n;
const timestampNs: bigint = dispatchTimestamp
? dispatchTimestamp.getValueNs()
- : realToElapsedTimeOffsetNs;
-
- if (type === TimestampType.ELAPSED) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(
- timestampNs - realToElapsedTimeOffsetNs,
- );
- } else if (type === TimestampType.REAL) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(timestampNs);
- }
-
- return undefined;
+ : realToBootTimeOffsetNs;
+ return this.timestampConverter.makeTimestampFromRealNs(timestampNs);
}
private compressEntries(
diff --git a/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions_test.ts b/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions_test.ts
index 971e64b..8d4927b 100644
--- a/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions_test.ts
+++ b/tools/winscope/src/parsers/transitions/legacy/traces_parser_transitions_test.ts
@@ -15,8 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -42,51 +41,14 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
- const timestamps = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- );
+ it('provides timestamps', () => {
+ const timestamps = assertDefined(parser.getTimestamps());
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(57649649922341n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(57651299086892n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
+ TimestampConverterUtils.makeRealTimestamp(1683130827957362976n),
+ TimestampConverterUtils.makeRealTimestamp(1683188477606574664n),
+ TimestampConverterUtils.makeRealTimestamp(1683188479255739215n),
];
expect(timestamps).toEqual(expected);
});
-
- it('provides real timestamps', () => {
- const timestamps = assertDefined(parser.getTimestamps(TimestampType.REAL));
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683130827957362976n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683188477607285317n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683188479256449868n),
- ];
- expect(timestamps).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getTracesParser(
- [
- 'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
- 'traces/elapsed_and_real_timestamp/shell_transition_trace.pb',
- ],
- true,
- )) as Parser<PropertyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(TraceType.TRANSITION);
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1683150627957362976n),
- );
- });
});
diff --git a/tools/winscope/src/parsers/transitions/operations/add_duration.ts b/tools/winscope/src/parsers/transitions/operations/add_duration.ts
index b001453..022325a 100644
--- a/tools/winscope/src/parsers/transitions/operations/add_duration.ts
+++ b/tools/winscope/src/parsers/transitions/operations/add_duration.ts
@@ -16,8 +16,8 @@
import {assertDefined} from 'common/assert_utils';
import {Timestamp} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TimeDuration} from 'common/time_duration';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {AddOperation} from 'trace/tree_node/operations/add_operation';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {DEFAULT_PROPERTY_TREE_NODE_FACTORY} from 'trace/tree_node/property_tree_node_factory';
@@ -39,10 +39,8 @@
return [];
}
- const timeDiffNs = finishTime.minus(sendTime).getValueNs();
-
- const timeDiff =
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(timeDiffNs);
+ const timeDiffNs = finishTime.minus(sendTime.getValueNs()).getValueNs();
+ const timeDiff = new TimeDuration(timeDiffNs);
const durationNode =
DEFAULT_PROPERTY_TREE_NODE_FACTORY.makeCalculatedProperty(
@@ -50,7 +48,7 @@
'duration',
timeDiff,
);
- durationNode.setFormatter(TIMESTAMP_FORMATTER);
+ durationNode.setFormatter(TIMESTAMP_NODE_FORMATTER);
return [durationNode];
}
diff --git a/tools/winscope/src/parsers/transitions/operations/add_duration_test.ts b/tools/winscope/src/parsers/transitions/operations/add_duration_test.ts
index 79485a2..2145274 100644
--- a/tools/winscope/src/parsers/transitions/operations/add_duration_test.ts
+++ b/tools/winscope/src/parsers/transitions/operations/add_duration_test.ts
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimeDuration} from 'common/time_duration';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {PropertySource} from 'trace/tree_node/property_tree_node';
import {AddDuration} from './add_duration';
describe('AddDuration', () => {
let operation: AddDuration;
- const TIMESTAMP_10 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n);
- const TIMESTAMP_30 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(30n);
+ const TIMESTAMP_10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const TIMESTAMP_30 = TimestampConverterUtils.makeRealTimestamp(30n);
beforeEach(() => {
operation = new AddDuration();
@@ -59,9 +60,9 @@
},
{
name: 'duration',
- value: NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(20n),
+ value: new TimeDuration(20n),
source: PropertySource.CALCULATED,
- formatter: TIMESTAMP_FORMATTER,
+ formatter: TIMESTAMP_NODE_FORMATTER,
},
])
.build();
diff --git a/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp.ts b/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp.ts
index db1a8ae..0b9ba10 100644
--- a/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp.ts
+++ b/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp.ts
@@ -19,9 +19,9 @@
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {DEFAULT_PROPERTY_TREE_NODE_FACTORY} from 'trace/tree_node/property_tree_node_factory';
-export class AddRealToElapsedTimeOffsetTimestamp extends AddOperation<PropertyTreeNode> {
+export class AddRealToBootTimeOffsetTimestamp extends AddOperation<PropertyTreeNode> {
constructor(
- private readonly realToElapsedTimeOffsetTimestamp: Timestamp | undefined,
+ private readonly realToBootTimeOffsetTimestamp: Timestamp | undefined,
) {
super();
}
@@ -31,8 +31,8 @@
const offsetNode =
DEFAULT_PROPERTY_TREE_NODE_FACTORY.makeCalculatedProperty(
value.id,
- 'realToElapsedTimeOffsetTimestamp',
- this.realToElapsedTimeOffsetTimestamp,
+ 'realToBootTimeOffsetTimestamp',
+ this.realToBootTimeOffsetTimestamp,
);
return [offsetNode];
diff --git a/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp_test.ts b/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp_test.ts
index a886fc0..15b0484 100644
--- a/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp_test.ts
+++ b/tools/winscope/src/parsers/transitions/operations/add_real_to_elapsed_time_offset_timestamp_test.ts
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {PropertySource} from 'trace/tree_node/property_tree_node';
-import {AddRealToElapsedTimeOffsetTimestamp} from './add_real_to_elapsed_time_offset_timestamp';
+import {AddRealToBootTimeOffsetTimestamp} from './add_real_to_elapsed_time_offset_timestamp';
-describe('AddRealToElapsedTimeOffsetTimestamp', () => {
+describe('AddRealToBootTimeOffsetTimestamp', () => {
it('adds undefined offset', () => {
const propertyRoot = new PropertyTreeBuilder()
.setIsRoot(true)
@@ -33,21 +33,23 @@
.setName('transition')
.setChildren([
{
- name: 'realToElapsedTimeOffsetTimestamp',
+ name: 'realToBootTimeOffsetTimestamp',
value: undefined,
source: PropertySource.CALCULATED,
},
])
.build();
- const operation = new AddRealToElapsedTimeOffsetTimestamp(undefined);
+ const operation = new AddRealToBootTimeOffsetTimestamp(undefined);
operation.apply(propertyRoot);
expect(propertyRoot).toEqual(expectedRoot);
});
it('adds offset timestamp', () => {
- const realToElapsedTimeOffsetTimestamp =
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(12345n);
+ const realToBootTimeOffsetTimestamp =
+ TimestampConverterUtils.TIMESTAMP_CONVERTER.makeTimestampFromMonotonicNs(
+ 12345n,
+ );
const propertyRoot = new PropertyTreeBuilder()
.setIsRoot(true)
.setRootId('TransitionsTraceEntry')
@@ -60,15 +62,15 @@
.setName('transition')
.setChildren([
{
- name: 'realToElapsedTimeOffsetTimestamp',
- value: realToElapsedTimeOffsetTimestamp,
+ name: 'realToBootTimeOffsetTimestamp',
+ value: realToBootTimeOffsetTimestamp,
source: PropertySource.CALCULATED,
},
])
.build();
- const operation = new AddRealToElapsedTimeOffsetTimestamp(
- realToElapsedTimeOffsetTimestamp,
+ const operation = new AddRealToBootTimeOffsetTimestamp(
+ realToBootTimeOffsetTimestamp,
);
operation.apply(propertyRoot);
expect(propertyRoot).toEqual(expectedRoot);
diff --git a/tools/winscope/src/parsers/transitions/operations/add_status_test.ts b/tools/winscope/src/parsers/transitions/operations/add_status_test.ts
index 470cbe2..38eef98 100644
--- a/tools/winscope/src/parsers/transitions/operations/add_status_test.ts
+++ b/tools/winscope/src/parsers/transitions/operations/add_status_test.ts
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {PropertySource} from 'trace/tree_node/property_tree_node';
import {AddStatus} from './add_status';
describe('AddStatus', () => {
let operation: AddStatus;
- const time0 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n);
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n);
+ const time0 = TimestampConverterUtils.makeRealTimestamp(0n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
beforeEach(() => {
operation = new AddStatus();
diff --git a/tools/winscope/src/parsers/transitions/parser_transitions_utils.ts b/tools/winscope/src/parsers/transitions/parser_transitions_utils.ts
index ed4e527..ed7570a 100644
--- a/tools/winscope/src/parsers/transitions/parser_transitions_utils.ts
+++ b/tools/winscope/src/parsers/transitions/parser_transitions_utils.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
import {
@@ -30,12 +30,12 @@
import {
EnumFormatter,
PropertyFormatter,
- TIMESTAMP_FORMATTER,
+ TIMESTAMP_NODE_FORMATTER,
} from 'trace/tree_node/formatters';
import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {AddDuration} from './operations/add_duration';
-import {AddRealToElapsedTimeOffsetTimestamp} from './operations/add_real_to_elapsed_time_offset_timestamp';
+import {AddRealToBootTimeOffsetTimestamp} from './operations/add_real_to_elapsed_time_offset_timestamp';
import {AddRootProperties} from './operations/add_root_properties';
import {AddStatus} from './operations/add_status';
import {UpdateAbortTimeNodes} from './operations/update_abort_time_nodes';
@@ -46,9 +46,8 @@
| com.android.server.wm.shell.ITransition
| com.android.wm.shell.ITransition
| perfetto.protos.IShellTransition;
- realToElapsedTimeOffsetNs: bigint | undefined;
- timestampType: TimestampType;
- timestampFactory: TimestampFactory;
+ realToBootTimeOffsetNs: bigint | undefined;
+ timestampConverter: ParserTimestampConverter;
handlerMapping?: {[key: number]: string};
}
@@ -122,19 +121,19 @@
);
}
- const realToElapsedTimeOffsetTimestamp =
- info.realToElapsedTimeOffsetNs !== undefined
- ? info.timestampFactory.makeTimestampFromType(
- info.timestampType,
- info.realToElapsedTimeOffsetNs,
- 0n,
- )
- : undefined;
+ let realToBootTimeOffsetTimestamp: Timestamp | undefined;
+
+ if (info.realToBootTimeOffsetNs !== undefined) {
+ realToBootTimeOffsetTimestamp =
+ info.timestampConverter.makeTimestampFromRealNs(
+ info.realToBootTimeOffsetNs,
+ );
+ }
const wmDataNode = assertDefined(tree.getChildByName('wmData'));
- new AddRealToElapsedTimeOffsetTimestamp(
- realToElapsedTimeOffsetTimestamp,
- ).apply(wmDataNode);
+ new AddRealToBootTimeOffsetTimestamp(realToBootTimeOffsetTimestamp).apply(
+ wmDataNode,
+ );
ParserTransitionsUtils.WM_ADD_DEFAULTS_OPERATION.apply(wmDataNode);
new TransformToTimestamp(
[
@@ -144,38 +143,23 @@
'finishTimeNs',
'startingWindowRemoveTimeNs',
],
- ParserTransitionsUtils.makeTimestampStrategy(info),
+ ParserTransitionsUtils.makeTimestampStrategy(info.timestampConverter),
).apply(wmDataNode);
const customFormatters = new Map<string, PropertyFormatter>([
['type', ParserTransitionsUtils.TRANSITION_TYPE_FORMATTER],
['mode', ParserTransitionsUtils.TRANSITION_TYPE_FORMATTER],
- ['abortTimeNs', TIMESTAMP_FORMATTER],
- ['createTimeNs', TIMESTAMP_FORMATTER],
- ['sendTimeNs', TIMESTAMP_FORMATTER],
- ['finishTimeNs', TIMESTAMP_FORMATTER],
- ['startingWindowRemoveTimeNs', TIMESTAMP_FORMATTER],
+ ['abortTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['createTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['sendTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['finishTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['startingWindowRemoveTimeNs', TIMESTAMP_NODE_FORMATTER],
]);
new SetFormatters(undefined, customFormatters).apply(tree);
return tree;
}
- private static makeTimestampStrategy(
- info: TransitionInfo,
- ): MakeTimestampStrategyType {
- if (info.timestampType === TimestampType.REAL) {
- return (valueNs: bigint) => {
- return info.timestampFactory.makeRealTimestamp(
- valueNs,
- info.realToElapsedTimeOffsetNs,
- );
- };
- } else {
- return info.timestampFactory.makeElapsedTimestamp;
- }
- }
-
static makeShellPropertiesTree(
info?: TransitionInfo,
denylistProperties: string[] = [],
@@ -199,31 +183,30 @@
);
}
- const realToElapsedTimeOffsetTimestamp =
- info.realToElapsedTimeOffsetNs !== undefined
- ? info.timestampFactory.makeTimestampFromType(
- info.timestampType,
- info.realToElapsedTimeOffsetNs,
- 0n,
- )
- : undefined;
+ let realToBootTimeOffsetTimestamp: Timestamp | undefined;
+ if (info.realToBootTimeOffsetNs !== undefined) {
+ realToBootTimeOffsetTimestamp =
+ info.timestampConverter.makeTimestampFromRealNs(
+ info.realToBootTimeOffsetNs,
+ );
+ }
const shellDataNode = assertDefined(tree.getChildByName('shellData'));
- new AddRealToElapsedTimeOffsetTimestamp(
- realToElapsedTimeOffsetTimestamp,
- ).apply(shellDataNode);
+ new AddRealToBootTimeOffsetTimestamp(realToBootTimeOffsetTimestamp).apply(
+ shellDataNode,
+ );
new TransformToTimestamp(
['dispatchTimeNs', 'mergeRequestTimeNs', 'mergeTimeNs', 'abortTimeNs'],
- ParserTransitionsUtils.makeTimestampStrategy(info),
+ ParserTransitionsUtils.makeTimestampStrategy(info.timestampConverter),
).apply(shellDataNode);
const customFormatters = new Map<string, PropertyFormatter>([
['type', ParserTransitionsUtils.TRANSITION_TYPE_FORMATTER],
['mode', ParserTransitionsUtils.TRANSITION_TYPE_FORMATTER],
- ['dispatchTimeNs', TIMESTAMP_FORMATTER],
- ['mergeRequestTimeNs', TIMESTAMP_FORMATTER],
- ['mergeTimeNs', TIMESTAMP_FORMATTER],
- ['abortTimeNs', TIMESTAMP_FORMATTER],
+ ['dispatchTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['mergeRequestTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['mergeTimeNs', TIMESTAMP_NODE_FORMATTER],
+ ['abortTimeNs', TIMESTAMP_NODE_FORMATTER],
]);
if (info.handlerMapping) {
@@ -234,4 +217,12 @@
return tree;
}
+
+ private static makeTimestampStrategy(
+ timestampConverter: ParserTimestampConverter,
+ ): MakeTimestampStrategyType {
+ return (valueNs: bigint) => {
+ return timestampConverter.makeTimestampFromBootTimeNs(valueNs);
+ };
+ }
}
diff --git a/tools/winscope/src/parsers/transitions/perfetto/parser_transitions.ts b/tools/winscope/src/parsers/transitions/perfetto/parser_transitions.ts
index bdaecde..8d6c26e 100644
--- a/tools/winscope/src/parsers/transitions/perfetto/parser_transitions.ts
+++ b/tools/winscope/src/parsers/transitions/perfetto/parser_transitions.ts
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
import {AbstractParser} from 'parsers/perfetto/abstract_parser';
import {FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder';
import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
@@ -29,12 +28,8 @@
return TraceType.TRANSITION;
}
- override async getEntry(
- index: number,
- timestampType: TimestampType,
- ): Promise<PropertyTreeNode> {
- const transitionProto = await this.queryTransition(index);
-
+ override async getEntry(index: number): Promise<PropertyTreeNode> {
+ const transitionProto = await this.queryEntry(index);
if (this.handlerIdToName === undefined) {
const handlers = await this.queryHandlers();
this.handlerIdToName = {};
@@ -42,26 +37,56 @@
(it) => (assertDefined(this.handlerIdToName)[it.id] = it.name),
);
}
-
- return this.makePropertiesTree(timestampType, transitionProto);
+ return this.makePropertiesTree(transitionProto);
}
protected override getTableName(): string {
return 'window_manager_shell_transitions';
}
+ protected override async queryEntry(
+ index: number,
+ ): Promise<perfetto.protos.ShellTransition> {
+ const protoBuilder = new FakeProtoBuilder();
+
+ const sql = `
+ SELECT
+ transitions.transition_id,
+ args.key,
+ args.value_type,
+ args.int_value,
+ args.string_value,
+ args.real_value
+ FROM
+ window_manager_shell_transitions as transitions
+ INNER JOIN args ON transitions.arg_set_id = args.arg_set_id
+ WHERE transitions.id = ${index};
+ `;
+ const result = await this.traceProcessor.query(sql).waitAllRows();
+
+ for (const it = result.iter({}); it.valid(); it.next()) {
+ protoBuilder.addArg(
+ it.get('key') as string,
+ it.get('value_type') as string,
+ it.get('int_value') as bigint | undefined,
+ it.get('real_value') as number | undefined,
+ it.get('string_value') as string | undefined,
+ );
+ }
+
+ return protoBuilder.build();
+ }
+
private makePropertiesTree(
- timestampType: TimestampType,
transitionProto: perfetto.protos.ShellTransition,
): PropertyTreeNode {
this.validatePerfettoTransition(transitionProto);
const perfettoTransitionInfo = {
entry: transitionProto,
- realToElapsedTimeOffsetNs: assertDefined(this.realToElapsedTimeOffsetNs),
- timestampType,
+ realToBootTimeOffsetNs: undefined,
handlerMapping: this.handlerIdToName,
- timestampFactory: this.timestampFactory,
+ timestampConverter: this.timestampConverter,
};
const shellEntryTree = ParserTransitionsUtils.makeShellPropertiesTree(
@@ -97,39 +122,6 @@
);
}
- private async queryTransition(
- index: number,
- ): Promise<perfetto.protos.ShellTransition> {
- const protoBuilder = new FakeProtoBuilder();
-
- const sql = `
- SELECT
- transitions.transition_id,
- args.key,
- args.value_type,
- args.int_value,
- args.string_value,
- args.real_value
- FROM
- window_manager_shell_transitions as transitions
- INNER JOIN args ON transitions.arg_set_id = args.arg_set_id
- WHERE transitions.id = ${index};
- `;
- const result = await this.traceProcessor.query(sql).waitAllRows();
-
- for (const it = result.iter({}); it.valid(); it.next()) {
- protoBuilder.addArg(
- it.get('key') as string,
- it.get('value_type') as string,
- it.get('int_value') as bigint | undefined,
- it.get('real_value') as number | undefined,
- it.get('string_value') as string | undefined,
- );
- }
-
- return protoBuilder.build();
- }
-
private async queryHandlers(): Promise<TransitionHandler[]> {
const sql =
'SELECT handler_id, handler_name FROM window_manager_shell_transition_handlers;';
diff --git a/tools/winscope/src/parsers/transitions/perfetto/parser_transitions_test.ts b/tools/winscope/src/parsers/transitions/perfetto/parser_transitions_test.ts
index c773f12..f5d4e79 100644
--- a/tools/winscope/src/parsers/transitions/perfetto/parser_transitions_test.ts
+++ b/tools/winscope/src/parsers/transitions/perfetto/parser_transitions_test.ts
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
import {Parser} from 'trace/parser';
@@ -42,45 +42,31 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(479602824452n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(480676958445n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(487195167758n),
+ TimestampConverterUtils.makeRealTimestamp(1700573425448299306n),
+ TimestampConverterUtils.makeRealTimestamp(1700573426522433299n),
+ TimestampConverterUtils.makeRealTimestamp(1700573433040642612n),
];
- const actual = assertDefined(
- parser.getTimestamps(TimestampType.ELAPSED),
- ).slice(0, 3);
- expect(actual).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1700573903102738218n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1700573904176872211n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1700573910695081524n),
- ];
- const actual = assertDefined(
- parser.getTimestamps(TimestampType.REAL),
- ).slice(0, 3);
+ const actual = assertDefined(parser.getTimestamps()).slice(0, 3);
expect(actual).toEqual(expected);
});
it('decodes transition properties', async () => {
- const entry = await parser.getEntry(0, TimestampType.REAL);
+ const entry = await parser.getEntry(0);
const wmDataNode = assertDefined(entry.getChildByName('wmData'));
const shellDataNode = assertDefined(entry.getChildByName('shellData'));
expect(entry.getChildByName('id')?.getValue()).toEqual(32n);
expect(
wmDataNode.getChildByName('createTimeNs')?.formattedValue(),
- ).toEqual('2023-11-21T13:38:23.083364560');
+ ).toEqual('2023-11-21T13:30:25.428925648');
expect(wmDataNode.getChildByName('sendTimeNs')?.formattedValue()).toEqual(
- '2023-11-21T13:38:23.096319557',
+ '2023-11-21T13:30:25.441880645',
);
expect(
wmDataNode.getChildByName('finishTimeNs')?.formattedValue(),
- ).toEqual('2023-11-21T13:38:23.624691628');
+ ).toEqual('2023-11-21T13:30:25.970252716');
expect(entry.getChildByName('merged')?.getValue()).toBeFalse();
expect(entry.getChildByName('played')?.getValue()).toBeTrue();
expect(entry.getChildByName('aborted')?.getValue()).toBeFalse();
@@ -89,7 +75,7 @@
assertDefined(
wmDataNode.getChildByName('startingWindowRemoveTimeNs'),
).formattedValue(),
- ).toEqual('2023-11-21T13:38:23.219566424');
+ ).toEqual('2023-11-21T13:30:25.565127512');
expect(
assertDefined(
wmDataNode.getChildByName('startTransactionId'),
@@ -125,7 +111,7 @@
assertDefined(
shellDataNode.getChildByName('dispatchTimeNs'),
).formattedValue(),
- ).toEqual('2023-11-21T13:38:23.102738218');
+ ).toEqual('2023-11-21T13:30:25.448299306');
expect(shellDataNode.getChildByName('mergeRequestTime')).toBeUndefined();
expect(shellDataNode.getChildByName('mergeTime')).toBeUndefined();
expect(shellDataNode.getChildByName('abortTimeNs')).toBeUndefined();
@@ -134,58 +120,5 @@
assertDefined(shellDataNode.getChildByName('handler')).formattedValue(),
).toEqual('com.android.wm.shell.transition.DefaultMixedHandler');
});
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = await UnitTestUtils.getPerfettoParser(
- TraceType.TRANSITION,
- 'traces/perfetto/shell_transitions_trace.perfetto-trace',
- true,
- );
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.TRANSITION,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(479602824452n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1700593703102738218n),
- );
-
- const entry = await parserWithTimezoneInfo.getEntry(
- 0,
- TimestampType.REAL,
- );
- const wmDataNode = assertDefined(entry.getChildByName('wmData'));
- const shellDataNode = assertDefined(entry.getChildByName('shellData'));
-
- expect(
- wmDataNode.getChildByName('createTimeNs')?.formattedValue(),
- ).toEqual('2023-11-21T19:08:23.083364560');
- expect(wmDataNode.getChildByName('sendTimeNs')?.formattedValue()).toEqual(
- '2023-11-21T19:08:23.096319557',
- );
- expect(
- wmDataNode.getChildByName('finishTimeNs')?.formattedValue(),
- ).toEqual('2023-11-21T19:08:23.624691628');
-
- expect(
- assertDefined(
- wmDataNode.getChildByName('startingWindowRemoveTimeNs'),
- ).formattedValue(),
- ).toEqual('2023-11-21T19:08:23.219566424');
-
- expect(
- assertDefined(
- shellDataNode.getChildByName('dispatchTimeNs'),
- ).formattedValue(),
- ).toEqual('2023-11-21T19:08:23.102738218');
- });
});
});
diff --git a/tools/winscope/src/parsers/view_capture/parser_view_capture.ts b/tools/winscope/src/parsers/view_capture/parser_view_capture.ts
index b0f9828..6de9409 100644
--- a/tools/winscope/src/parsers/view_capture/parser_view_capture.ts
+++ b/tools/winscope/src/parsers/view_capture/parser_view_capture.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {ParsingUtils} from 'parsers/legacy/parsing_utils';
import {com} from 'protos/viewcapture/latest/static';
import {Parser} from 'trace/parser';
@@ -30,7 +30,7 @@
constructor(
private readonly traceFile: TraceFile,
- private readonly timestampFactory: TimestampFactory,
+ private readonly timestampConverter: ParserTimestampConverter,
) {}
async parse() {
@@ -44,7 +44,7 @@
traceBuffer,
) as com.android.app.viewcapture.data.IExportedData;
- const realToElapsedTimeOffsetNs = BigInt(
+ const realToBootTimeOffsetNs = BigInt(
assertDefined(exportedData.realToElapsedTimeOffsetNanos).toString(),
);
@@ -55,10 +55,10 @@
[this.traceFile.getDescriptor()],
windowData.frameData ?? [],
ParserViewCapture.toTraceType(windowData),
- realToElapsedTimeOffsetNs,
+ realToBootTimeOffsetNs,
assertDefined(exportedData.package),
assertDefined(exportedData.classname),
- this.timestampFactory,
+ this.timestampConverter,
),
),
);
diff --git a/tools/winscope/src/parsers/view_capture/parser_view_capture_test.ts b/tools/winscope/src/parsers/view_capture/parser_view_capture_test.ts
index 4dd3799..4cacfe2 100644
--- a/tools/winscope/src/parsers/view_capture/parser_view_capture_test.ts
+++ b/tools/winscope/src/parsers/view_capture/parser_view_capture_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -50,56 +49,17 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(181114412436130n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(181114421012750n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(181114429047540n),
+ TimestampConverterUtils.makeRealTimestamp(1691692936292808460n),
+ TimestampConverterUtils.makeRealTimestamp(1691692936301385080n),
+ TimestampConverterUtils.makeRealTimestamp(1691692936309419870n),
];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1691692936292808460n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1691692936301385080n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1691692936309419870n),
- ];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.REAL)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/com.google.android.apps.nexuslauncher_0.vc',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.VIEW_CAPTURE_TASKBAR_DRAG_LAYER,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(181114412436130n),
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1691712736292808460n),
- );
+ expect(assertDefined(parser.getTimestamps()).slice(0, 3)).toEqual(expected);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ const entry = await parser.getEntry(1);
expect(entry.id).toEqual(
'ViewNode com.android.launcher3.taskbar.TaskbarDragLayer@265160962',
);
diff --git a/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts b/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
index 3d45330..49e4f13 100644
--- a/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
+++ b/tools/winscope/src/parsers/view_capture/parser_view_capture_window.ts
@@ -15,8 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {TimestampFactory} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {ParserTimestampConverter} from 'common/timestamp_converter';
import {AddDefaults} from 'parsers/operations/add_defaults';
import {SetFormatters} from 'parsers/operations/set_formatters';
import {TranslateIntDef} from 'parsers/operations/translate_intdef';
@@ -82,16 +82,16 @@
SetRootTransformProperties: new SetRootTransformProperties(),
};
- private timestamps = new Map<TimestampType, Timestamp[]>();
+ private timestamps: Timestamp[] | undefined;
constructor(
private readonly descriptors: string[],
private readonly frameData: com.android.app.viewcapture.data.IFrameData[],
private readonly traceType: TraceType,
- private readonly realToElapsedTimeOffsetNs: bigint,
+ private readonly realToBootTimeOffsetNs: bigint,
private readonly packageName: string,
private readonly classNames: string[],
- private readonly timestampFactory: TimestampFactory,
+ private readonly timestampConverter: ParserTimestampConverter,
) {
/*
TODO: Enable this once multiple ViewCapture Tabs becomes generic. Right now it doesn't matter since
@@ -101,11 +101,10 @@
windowData.title
)}`;
*/
- this.parse();
}
parse() {
- this.timestamps = this.decodeTimestamps();
+ throw new Error('Not implemented');
}
getTraceType(): TraceType {
@@ -120,11 +119,23 @@
return this.frameData.length;
}
- getTimestamps(type: TimestampType): Timestamp[] | undefined {
- return this.timestamps.get(type);
+ getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
}
- getEntry(index: number, _: TimestampType): Promise<HierarchyTreeNode> {
+ getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ createTimestamps() {
+ this.timestamps = this.decodeTimestamps();
+ }
+
+ getTimestamps(): Timestamp[] | undefined {
+ return this.timestamps;
+ }
+
+ getEntry(index: number): Promise<HierarchyTreeNode> {
const tree = this.makeHierarchyTree(this.frameData[index]);
return Promise.resolve(tree);
}
@@ -144,35 +155,12 @@
return this.descriptors;
}
- private decodeTimestamps(): Map<TimestampType, Timestamp[]> {
- const timestampMap = new Map<TimestampType, Timestamp[]>();
- for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
- const timestamps: Timestamp[] = [];
- let areTimestampsValid = true;
-
- for (const entry of this.frameData) {
- const timestampNs = BigInt(assertDefined(entry.timestamp).toString());
-
- let timestamp: Timestamp | undefined;
- if (this.timestampFactory.canMakeTimestampFromType(type, 0n)) {
- timestamp = this.timestampFactory.makeTimestampFromType(
- type,
- timestampNs,
- this.realToElapsedTimeOffsetNs,
- );
- }
- if (timestamp === undefined) {
- areTimestampsValid = false;
- break;
- }
- timestamps.push(timestamp);
- }
-
- if (areTimestampsValid) {
- timestampMap.set(type, timestamps);
- }
- }
- return timestampMap;
+ private decodeTimestamps(): Timestamp[] {
+ return this.frameData.map((entry) =>
+ this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.timestamp).toString()),
+ ),
+ );
}
private makeHierarchyTree(
diff --git a/tools/winscope/src/parsers/window_manager/parser_window_manager.ts b/tools/winscope/src/parsers/window_manager/parser_window_manager.ts
index 317f1f7..44f646d 100644
--- a/tools/winscope/src/parsers/window_manager/parser_window_manager.ts
+++ b/tools/winscope/src/parsers/window_manager/parser_window_manager.ts
@@ -15,7 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {com} from 'protos/windowmanager/latest/static';
import {
@@ -38,7 +38,7 @@
0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
]; // .WINTRACE
- private realToElapsedTimeOffsetNs: undefined | bigint;
+ private realToBootTimeOffsetNs: bigint | undefined;
override getTraceType(): TraceType {
return TraceType.WINDOW_MANAGER;
@@ -48,6 +48,14 @@
return ParserWindowManager.MAGIC_NUMBER;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.realToBootTimeOffsetNs;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): com.android.server.wm.IWindowManagerTraceProto[] {
@@ -57,35 +65,20 @@
const timeOffset = BigInt(
decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0',
);
- this.realToElapsedTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
+ this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
return decoded.entry ?? [];
}
- override getTimestamp(
- type: TimestampType,
+ protected override getTimestamp(
entry: com.android.server.wm.IWindowManagerTraceProto,
- ): undefined | Timestamp {
- const elapsedRealtimeNanos = BigInt(
- assertDefined(entry.elapsedRealtimeNanos).toString(),
+ ): Timestamp {
+ return this.timestampConverter.makeTimestampFromBootTimeNs(
+ BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()),
);
- if (
- this.timestampFactory.canMakeTimestampFromType(
- type,
- this.realToElapsedTimeOffsetNs,
- )
- ) {
- return this.timestampFactory.makeTimestampFromType(
- type,
- elapsedRealtimeNanos,
- this.realToElapsedTimeOffsetNs,
- );
- }
- return undefined;
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entry: com.android.server.wm.IWindowManagerTraceProto,
): HierarchyTreeNode {
return this.makeHierarchyTree(entry);
diff --git a/tools/winscope/src/parsers/window_manager/parser_window_manager_dump.ts b/tools/winscope/src/parsers/window_manager/parser_window_manager_dump.ts
index deacbed..f6ec063 100644
--- a/tools/winscope/src/parsers/window_manager/parser_window_manager_dump.ts
+++ b/tools/winscope/src/parsers/window_manager/parser_window_manager_dump.ts
@@ -15,8 +15,7 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
import {AbstractParser} from 'parsers/legacy/abstract_parser';
import {com} from 'protos/windowmanager/latest/static';
import {
@@ -43,6 +42,14 @@
return undefined;
}
+ override getRealToBootTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
+ override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return undefined;
+ }
+
override decodeTrace(
buffer: Uint8Array,
): com.android.server.wm.IWindowManagerServiceDumpProto[] {
@@ -55,28 +62,19 @@
// 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,
- TimestampType.ELAPSED /*irrelevant for dump*/,
- entryProto,
- );
+ this.processDecodedEntry(0, entryProto);
return [entryProto];
}
- override getTimestamp(
- type: TimestampType,
- entryProto: any,
- ): undefined | Timestamp {
- if (NO_TIMEZONE_OFFSET_FACTORY.canMakeTimestampFromType(type, 0n)) {
- return NO_TIMEZONE_OFFSET_FACTORY.makeTimestampFromType(type, 0n, 0n);
- }
- return undefined;
+ protected override getTimestamp(
+ entryProto: com.android.server.wm.IWindowManagerServiceDumpProto,
+ ): Timestamp {
+ return this.timestampConverter.makeZeroTimestamp();
}
override processDecodedEntry(
index: number,
- timestampType: TimestampType,
entryProto: com.android.server.wm.IWindowManagerServiceDumpProto,
): HierarchyTreeNode {
return this.makeHierarchyTree(entryProto);
diff --git a/tools/winscope/src/parsers/window_manager/parser_window_manager_dump_test.ts b/tools/winscope/src/parsers/window_manager/parser_window_manager_dump_test.ts
index 76fc8eb..4c585f4 100644
--- a/tools/winscope/src/parsers/window_manager/parser_window_manager_dump_test.ts
+++ b/tools/winscope/src/parsers/window_manager/parser_window_manager_dump_test.ts
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -29,6 +29,7 @@
let trace: Trace<HierarchyTreeNode>;
beforeAll(async () => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
parser = (await UnitTestUtils.getParser(
'traces/dump_WindowManager.pb',
)) as Parser<HierarchyTreeNode>;
@@ -46,35 +47,27 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamp (always zero)', () => {
- const expected = [NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n)];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
- });
-
- it('provides real timestamp (always zero)', () => {
- const expected = [NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n)];
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual(expected);
+ it('provides timestamp (always zero)', () => {
+ const expected = [TimestampConverterUtils.makeElapsedTimestamp(0n)];
+ expect(parser.getTimestamps()).toEqual(expected);
});
it('does not apply timezone info', async () => {
const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
'traces/dump_WindowManager.pb',
- true,
+ UnitTestUtils.getTimestampConverter(true),
)) as Parser<HierarchyTreeNode>;
expect(parserWithTimezoneInfo.getTraceType()).toEqual(
TraceType.WINDOW_MANAGER,
);
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(0n),
- ]);
- expect(parser.getTimestamps(TimestampType.REAL)).toEqual([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
+ expect(parser.getTimestamps()).toEqual([
+ TimestampConverterUtils.makeElapsedTimestamp(0n),
]);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(0, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(0);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.getEagerPropertyByName('focusedApp')?.getValue()).toEqual(
'com.google.android.apps.nexuslauncher/.NexusLauncherActivity',
diff --git a/tools/winscope/src/parsers/window_manager/parser_window_manager_test.ts b/tools/winscope/src/parsers/window_manager/parser_window_manager_test.ts
index b5db515..c2e2bea 100644
--- a/tools/winscope/src/parsers/window_manager/parser_window_manager_test.ts
+++ b/tools/winscope/src/parsers/window_manager/parser_window_manager_test.ts
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
import {CoarseVersion} from 'trace/coarse_version';
@@ -26,7 +25,7 @@
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
describe('ParserWindowManager', () => {
- describe('trace with elapsed + real timestamp', () => {
+ describe('trace with real timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
let trace: Trace<HierarchyTreeNode>;
@@ -49,53 +48,19 @@
expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LEGACY);
});
- it('provides elapsed timestamps', () => {
+ it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14474594000n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15398076788n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(15409222011n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089075566202n),
+ TimestampConverterUtils.makeRealTimestamp(1659107089999048990n),
+ TimestampConverterUtils.makeRealTimestamp(1659107090010194213n),
];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.ELAPSED)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('provides real timestamps', () => {
- const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089075566202n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107089999048990n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659107090010194213n),
- ];
- expect(
- assertDefined(parser.getTimestamps(TimestampType.REAL)).slice(0, 3),
- ).toEqual(expected);
- });
-
- it('applies timezone info to real timestamps only', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_and_real_timestamp/WindowManager.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.WINDOW_MANAGER,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(14474594000n));
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.REAL),
- )[0],
- ).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889075566202n),
+ expect(assertDefined(parser.getTimestamps()).slice(0, 3)).toEqual(
+ expected,
);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(1, TimestampType.REAL);
+ const entry = await parser.getEntry(1);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('WindowManagerState root');
});
@@ -109,7 +74,7 @@
});
});
- describe('trace elapsed (only) timestamp', () => {
+ describe('trace with only elapsed timestamps', () => {
let parser: Parser<HierarchyTreeNode>;
beforeAll(async () => {
@@ -124,31 +89,15 @@
it('provides timestamps', () => {
const expected = [
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850254319343n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850763506110n),
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850782750048n),
+ TimestampConverterUtils.makeElapsedTimestamp(850254319343n),
+ TimestampConverterUtils.makeElapsedTimestamp(850763506110n),
+ TimestampConverterUtils.makeElapsedTimestamp(850782750048n),
];
- expect(parser.getTimestamps(TimestampType.ELAPSED)).toEqual(expected);
- });
-
- it('does not apply timezone info', async () => {
- const parserWithTimezoneInfo = (await UnitTestUtils.getParser(
- 'traces/elapsed_timestamp/WindowManager.pb',
- true,
- )) as Parser<HierarchyTreeNode>;
- expect(parserWithTimezoneInfo.getTraceType()).toEqual(
- TraceType.WINDOW_MANAGER,
- );
-
- expect(
- assertDefined(
- parserWithTimezoneInfo.getTimestamps(TimestampType.ELAPSED),
- )[0],
- ).toEqual(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(850254319343n));
+ expect(parser.getTimestamps()).toEqual(expected);
});
it('retrieves trace entry', async () => {
- const entry = await parser.getEntry(1, TimestampType.ELAPSED);
+ const entry = await parser.getEntry(1);
expect(entry).toBeInstanceOf(HierarchyTreeNode);
expect(entry.id).toEqual('WindowManagerState root');
});
diff --git a/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
index c02add5..95285e2 100644
--- a/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
+++ b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-UPB2.230407.019-2023-05-30-14-33-48.txt
@@ -3,4 +3,6 @@
========================================================
# ORIGINAL CONTENTS REMOVED TO AVOID INFORMATION LEAKS
-
+[persist.sys.locale]: [en-US]
+[persist.sys.timezone]: [Asia/Kolkata]
+[persist.sys.time.offset]: [19800000]
diff --git a/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-no-time-offset-UPB2.230407.019-2023-05-30-14-33-48.txt b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-no-time-offset-UPB2.230407.019-2023-05-30-14-33-48.txt
new file mode 100644
index 0000000..aaa239b
--- /dev/null
+++ b/tools/winscope/src/test/fixtures/bugreports/bugreport-codename_beta-no-time-offset-UPB2.230407.019-2023-05-30-14-33-48.txt
@@ -0,0 +1,7 @@
+========================================================
+== dumpstate: 2023-05-30 14:33:48
+========================================================
+
+# ORIGINAL CONTENTS REMOVED TO AVOID INFORMATION LEAKS
+[persist.sys.locale]: [en-US]
+[persist.sys.timezone]: [Asia/Kolkata]
diff --git a/tools/winscope/src/test/fixtures/bugreports/dumpstate_board.txt b/tools/winscope/src/test/fixtures/bugreports/dumpstate_board.txt
deleted file mode 100644
index 7d78918..0000000
--- a/tools/winscope/src/test/fixtures/bugreports/dumpstate_board.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-TEST DUMPSTATE BOARD
-[persist.sys.locale]: [en-US]
-[persist.sys.timezone]: [Asia/Kolkata]
-EOF
\ No newline at end of file
diff --git a/tools/winscope/src/test/unit/parser_builder.ts b/tools/winscope/src/test/unit/parser_builder.ts
index 4d19e4f..74dd008 100644
--- a/tools/winscope/src/test/unit/parser_builder.ts
+++ b/tools/winscope/src/test/unit/parser_builder.ts
@@ -15,7 +15,7 @@
*/
import {Timestamp} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {
CustomQueryParserResultTypeMap,
CustomQueryType,
@@ -30,6 +30,7 @@
private timestamps?: Timestamp[];
private customQueryResult = new Map<CustomQueryType, {}>();
private descriptors = ['file descriptor'];
+ private noOffsets = false;
setType(type: TraceType): this {
this.type = type;
@@ -46,6 +47,11 @@
return this;
}
+ setNoOffsets(value: boolean): this {
+ this.noOffsets = value;
+ return this;
+ }
+
setCustomQueryResult<Q extends CustomQueryType>(
type: Q,
result: CustomQueryParserResultTypeMap[Q],
@@ -86,13 +92,14 @@
this.entries,
this.customQueryResult,
this.descriptors,
+ this.noOffsets,
);
}
private createTimestamps(entries: T[]): Timestamp[] {
const timestamps = new Array<Timestamp>();
for (let i = 0; i < entries.length; ++i) {
- timestamps[i] = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(BigInt(i));
+ timestamps[i] = TimestampConverterUtils.makeRealTimestamp(BigInt(i));
}
return timestamps;
}
diff --git a/tools/winscope/src/test/unit/timestamp_converter_utils.ts b/tools/winscope/src/test/unit/timestamp_converter_utils.ts
new file mode 100644
index 0000000..3b5c017
--- /dev/null
+++ b/tools/winscope/src/test/unit/timestamp_converter_utils.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Timestamp} from 'common/time';
+import {TimestampConverter} from 'common/timestamp_converter';
+
+export class TimestampConverterUtils {
+ static readonly ASIA_TIMEZONE_INFO = {
+ timezone: 'Asia/Kolkata',
+ locale: 'en-US',
+ utcOffsetMs: 19800000,
+ };
+ static readonly UTC_TIMEZONE_INFO = {
+ timezone: 'UTC',
+ locale: 'en-US',
+ utcOffsetMs: 0,
+ };
+
+ static readonly TIMESTAMP_CONVERTER_WITH_UTC_OFFSET = new TimestampConverter(
+ TimestampConverterUtils.ASIA_TIMEZONE_INFO,
+ 0n,
+ 0n,
+ );
+
+ static readonly TIMESTAMP_CONVERTER = new TimestampConverter(
+ TimestampConverterUtils.UTC_TIMEZONE_INFO,
+ 0n,
+ 0n,
+ );
+
+ private static readonly TIMESTAMP_CONVERTER_NO_RTE_OFFSET =
+ new TimestampConverter({
+ timezone: 'UTC',
+ locale: 'en-US',
+ utcOffsetMs: 0,
+ });
+
+ static makeRealTimestamp(valueNs: bigint): Timestamp {
+ return TimestampConverterUtils.TIMESTAMP_CONVERTER.makeTimestampFromRealNs(
+ valueNs,
+ );
+ }
+
+ static makeRealTimestampWithUTCOffset(valueNs: bigint): Timestamp {
+ return TimestampConverterUtils.TIMESTAMP_CONVERTER_WITH_UTC_OFFSET.makeTimestampFromRealNs(
+ valueNs,
+ );
+ }
+
+ static makeElapsedTimestamp(valueNs: bigint): Timestamp {
+ return TimestampConverterUtils.TIMESTAMP_CONVERTER_NO_RTE_OFFSET.makeTimestampFromMonotonicNs(
+ valueNs,
+ );
+ }
+}
diff --git a/tools/winscope/src/test/unit/trace_builder.ts b/tools/winscope/src/test/unit/trace_builder.ts
index 6319cad..9be7037 100644
--- a/tools/winscope/src/test/unit/trace_builder.ts
+++ b/tools/winscope/src/test/unit/trace_builder.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {
CustomQueryParserResultTypeMap,
CustomQueryType,
@@ -37,7 +37,6 @@
private parserCustomQueryResult = new Map<CustomQueryType, {}>();
private entries?: T[];
private timestamps?: Timestamp[];
- private timestampType = TimestampType.REAL;
private frameMap?: FrameMap;
private frameMapBuilder?: FrameMapBuilder;
private descriptors: string[] = [];
@@ -62,11 +61,6 @@
return this;
}
- setTimestampType(type: TimestampType): TraceBuilder<T> {
- this.timestampType = type;
- return this;
- }
-
setFrameMap(frameMap?: FrameMap): TraceBuilder<T> {
this.frameMap = frameMap;
return this;
@@ -105,7 +99,6 @@
this.parser,
this.descriptors,
undefined,
- this.timestampType,
entriesRange,
);
diff --git a/tools/winscope/src/test/unit/utils.ts b/tools/winscope/src/test/unit/utils.ts
index 8a806b4..541c582 100644
--- a/tools/winscope/src/test/unit/utils.ts
+++ b/tools/winscope/src/test/unit/utils.ts
@@ -15,11 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {Timestamp, TimestampType} from 'common/time';
-import {
- NO_TIMEZONE_OFFSET_FACTORY,
- TimestampFactory,
-} from 'common/timestamp_factory';
+import {Timestamp} from 'common/time';
+import {TimestampConverter} from 'common/timestamp_converter';
import {UrlUtils} from 'common/url_utils';
import {ParserFactory as LegacyParserFactory} from 'parsers/legacy/parser_factory';
import {TracesParserFactory} from 'parsers/legacy/traces_parser_factory';
@@ -30,14 +27,10 @@
import {TraceFile} from 'trace/trace_file';
import {TraceEntryTypeMap, TraceType} from 'trace/trace_type';
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
+import {TimestampConverterUtils} from './timestamp_converter_utils';
import {TraceBuilder} from './trace_builder';
class UnitTestUtils {
- static readonly TIMESTAMP_FACTORY_WITH_TIMEZONE = new TimestampFactory({
- timezone: 'Asia/Kolkata',
- locale: 'en-US',
- });
-
static async getFixtureFile(
srcFilename: string,
dstFilename: string = srcFilename,
@@ -54,7 +47,8 @@
type: T,
filename: string,
): Promise<Trace<T>> {
- const legacyParsers = await UnitTestUtils.getParsers(filename);
+ const converter = UnitTestUtils.getTimestampConverter(false);
+ const legacyParsers = await UnitTestUtils.getParsers(filename, converter);
expect(legacyParsers.length).toBeLessThanOrEqual(1);
if (legacyParsers.length === 1) {
expect(legacyParsers[0].getTraceType()).toEqual(type);
@@ -75,44 +69,28 @@
static async getParser(
filename: string,
- withTimezoneInfo = false,
+ converter = UnitTestUtils.getTimestampConverter(),
+ initializeRealToElapsedTimeOffsetNs = true,
): Promise<Parser<object>> {
- const parsers = await UnitTestUtils.getParsers(filename, withTimezoneInfo);
+ const parsers = await UnitTestUtils.getParsers(
+ filename,
+ converter,
+ initializeRealToElapsedTimeOffsetNs,
+ );
expect(parsers.length)
.withContext(`Should have been able to create a parser for ${filename}`)
.toBeGreaterThanOrEqual(1);
return parsers[0];
}
- static async getParsers(
- filename: string,
- withTimezoneInfo = false,
- ): Promise<Array<Parser<object>>> {
- const file = new TraceFile(
- await UnitTestUtils.getFixtureFile(filename),
- undefined,
- );
- const fileAndParsers = await new LegacyParserFactory().createParsers(
- [file],
- withTimezoneInfo
- ? UnitTestUtils.TIMESTAMP_FACTORY_WITH_TIMEZONE
- : NO_TIMEZONE_OFFSET_FACTORY,
- undefined,
- undefined,
- );
- return fileAndParsers.map((fileAndParser) => {
- return fileAndParser.parser;
- });
- }
-
static async getPerfettoParser<T extends TraceType>(
traceType: T,
fixturePath: string,
- withTimezoneInfo = false,
+ withUTCOffset = false,
): Promise<Parser<TraceEntryTypeMap[T]>> {
const parsers = await UnitTestUtils.getPerfettoParsers(
fixturePath,
- withTimezoneInfo,
+ withUTCOffset,
);
const parser = assertDefined(
parsers.find((parser) => parser.getTraceType() === traceType),
@@ -122,36 +100,60 @@
static async getPerfettoParsers(
fixturePath: string,
- withTimezoneInfo = false,
+ withUTCOffset = false,
): Promise<Array<Parser<object>>> {
const file = await UnitTestUtils.getFixtureFile(fixturePath);
const traceFile = new TraceFile(file);
- return await new PerfettoParserFactory().createParsers(
+ const converter = UnitTestUtils.getTimestampConverter(withUTCOffset);
+ const parsers = await new PerfettoParserFactory().createParsers(
traceFile,
- withTimezoneInfo
- ? UnitTestUtils.TIMESTAMP_FACTORY_WITH_TIMEZONE
- : NO_TIMEZONE_OFFSET_FACTORY,
+ converter,
undefined,
);
+ parsers.forEach((parser) => {
+ converter.setRealToBootTimeOffsetNs(
+ assertDefined(parser.getRealToBootTimeOffsetNs()),
+ );
+ parser.createTimestamps();
+ });
+ return parsers;
}
static async getTracesParser(
filenames: string[],
- withTimezoneInfo = false,
+ withUTCOffset = false,
): Promise<Parser<object>> {
+ const converter = UnitTestUtils.getTimestampConverter(withUTCOffset);
const parsersArray = await Promise.all(
- filenames.map((filename) =>
- UnitTestUtils.getParser(filename, withTimezoneInfo),
+ filenames.map(async (filename) =>
+ UnitTestUtils.getParser(filename, converter, true),
),
);
+ const offset = parsersArray
+ .filter((parser) => parser.getRealToBootTimeOffsetNs() !== undefined)
+ .sort((a, b) =>
+ Number(
+ (a.getRealToBootTimeOffsetNs() ?? 0n) -
+ (b.getRealToBootTimeOffsetNs() ?? 0n),
+ ),
+ )
+ .at(-1)
+ ?.getRealToBootTimeOffsetNs();
+
+ if (offset !== undefined) {
+ converter.setRealToBootTimeOffsetNs(offset);
+ }
const traces = new Traces();
parsersArray.forEach((parser) => {
- const trace = Trace.fromParser(parser, TimestampType.REAL);
+ const trace = Trace.fromParser(parser);
traces.setTrace(parser.getTraceType(), trace);
});
- const tracesParsers = await new TracesParserFactory().createParsers(traces);
+ const tracesParsers = await new TracesParserFactory().createParsers(
+ traces,
+ converter,
+ );
expect(tracesParsers.length)
.withContext(
`Should have been able to create a traces parser for [${filenames.join()}]`,
@@ -160,6 +162,12 @@
return tracesParsers[0];
}
+ static getTimestampConverter(withUTCOffset = false): TimestampConverter {
+ return withUTCOffset
+ ? new TimestampConverter(TimestampConverterUtils.ASIA_TIMEZONE_INFO)
+ : new TimestampConverter(TimestampConverterUtils.UTC_TIMEZONE_INFO);
+ }
+
static async getWindowManagerState(index = 0): Promise<HierarchyTreeNode> {
return UnitTestUtils.getTraceEntry(
'traces/elapsed_and_real_timestamp/WindowManager.pb',
@@ -194,7 +202,7 @@
const parser = (await UnitTestUtils.getParser(
'traces/ime/SurfaceFlinger_with_IME.pb',
)) as Parser<HierarchyTreeNode>;
- surfaceFlingerEntry = await parser.getEntry(5, TimestampType.ELAPSED);
+ surfaceFlingerEntry = await parser.getEntry(5);
}
let windowManagerEntry: HierarchyTreeNode | undefined;
@@ -202,7 +210,7 @@
const parser = (await UnitTestUtils.getParser(
'traces/ime/WindowManager_with_IME.pb',
)) as Parser<HierarchyTreeNode>;
- windowManagerEntry = await parser.getEntry(2, TimestampType.ELAPSED);
+ windowManagerEntry = await parser.getEntry(2);
}
const entries = new Map<TraceType, HierarchyTreeNode>();
@@ -237,14 +245,59 @@
timestamp: Timestamp,
expectedTimestamp: Timestamp,
): boolean {
- if (timestamp.getType() !== expectedTimestamp.getType()) return false;
- if (timestamp.getValueNs() !== expectedTimestamp.getValueNs()) return false;
+ if (timestamp.format() !== expectedTimestamp.format()) return false;
+ if (timestamp.getValueNs() !== expectedTimestamp.getValueNs()) {
+ return false;
+ }
return true;
}
private static async getTraceEntry<T>(filename: string, index = 0) {
const parser = (await UnitTestUtils.getParser(filename)) as Parser<T>;
- return parser.getEntry(index, TimestampType.ELAPSED);
+ return parser.getEntry(index);
+ }
+
+ private static async getParsers(
+ filename: string,
+ converter: TimestampConverter,
+ initializeRealToElapsedTimeOffsetNs = true,
+ ): Promise<Array<Parser<object>>> {
+ const file = new TraceFile(
+ await UnitTestUtils.getFixtureFile(filename),
+ undefined,
+ );
+ const fileAndParsers = await new LegacyParserFactory().createParsers(
+ [file],
+ converter,
+ undefined,
+ undefined,
+ );
+
+ if (initializeRealToElapsedTimeOffsetNs) {
+ const monotonicOffset = fileAndParsers
+ .find(
+ (fileAndParser) =>
+ fileAndParser.parser.getRealToMonotonicTimeOffsetNs() !== undefined,
+ )
+ ?.parser.getRealToMonotonicTimeOffsetNs();
+ if (monotonicOffset !== undefined) {
+ converter.setRealToMonotonicTimeOffsetNs(monotonicOffset);
+ }
+ const bootTimeOffset = fileAndParsers
+ .find(
+ (fileAndParser) =>
+ fileAndParser.parser.getRealToBootTimeOffsetNs() !== undefined,
+ )
+ ?.parser.getRealToBootTimeOffsetNs();
+ if (bootTimeOffset !== undefined) {
+ converter.setRealToBootTimeOffsetNs(bootTimeOffset);
+ }
+ }
+
+ return fileAndParsers.map((fileAndParser) => {
+ fileAndParser.parser.createTimestamps();
+ return fileAndParser.parser;
+ });
}
}
diff --git a/tools/winscope/src/trace/frame_mapper_test.ts b/tools/winscope/src/trace/frame_mapper_test.ts
index 1737e41..45b44aa 100644
--- a/tools/winscope/src/trace/frame_mapper_test.ts
+++ b/tools/winscope/src/trace/frame_mapper_test.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesUtils} from 'test/unit/traces_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {CustomQueryType} from './custom_query';
@@ -28,16 +28,16 @@
import {PropertyTreeNode} from './tree_node/property_tree_node';
describe('FrameMapper', () => {
- const time0 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n);
- const time1 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1n);
- const time2 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(2n);
- const time3 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(3n);
- const time4 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(4n);
- const time5 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n);
- const time6 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(6n);
- const time7 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(7n);
- const time8 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(8n);
- const time10seconds = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(
+ const time0 = TimestampConverterUtils.makeRealTimestamp(0n);
+ const time1 = TimestampConverterUtils.makeRealTimestamp(1n);
+ const time2 = TimestampConverterUtils.makeRealTimestamp(2n);
+ const time3 = TimestampConverterUtils.makeRealTimestamp(3n);
+ const time4 = TimestampConverterUtils.makeRealTimestamp(4n);
+ const time5 = TimestampConverterUtils.makeRealTimestamp(5n);
+ const time6 = TimestampConverterUtils.makeRealTimestamp(6n);
+ const time7 = TimestampConverterUtils.makeRealTimestamp(7n);
+ const time8 = TimestampConverterUtils.makeRealTimestamp(8n);
+ const time10seconds = TimestampConverterUtils.makeRealTimestamp(
10n * 1000000000n,
);
diff --git a/tools/winscope/src/trace/parser.ts b/tools/winscope/src/trace/parser.ts
index 79f797c..e2ac2e5 100644
--- a/tools/winscope/src/trace/parser.ts
+++ b/tools/winscope/src/trace/parser.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {CoarseVersion} from './coarse_version';
import {
CustomQueryParamTypeMap,
@@ -28,12 +28,15 @@
getCoarseVersion(): CoarseVersion;
getTraceType(): TraceType;
getLengthEntries(): number;
- getTimestamps(type: TimestampType): Timestamp[] | undefined;
- getEntry(index: AbsoluteEntryIndex, timestampType: TimestampType): Promise<T>;
+ getTimestamps(): Timestamp[] | undefined;
+ getEntry(index: AbsoluteEntryIndex): Promise<T>;
customQuery<Q extends CustomQueryType>(
type: Q,
entriesRange: EntriesRange,
param?: CustomQueryParamTypeMap[Q],
): Promise<CustomQueryParserResultTypeMap[Q]>;
getDescriptors(): string[];
+ getRealToMonotonicTimeOffsetNs(): bigint | undefined;
+ getRealToBootTimeOffsetNs(): bigint | undefined;
+ createTimestamps(): void;
}
diff --git a/tools/winscope/src/trace/parser_mock.ts b/tools/winscope/src/trace/parser_mock.ts
index afdde67..0ce8020 100644
--- a/tools/winscope/src/trace/parser_mock.ts
+++ b/tools/winscope/src/trace/parser_mock.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {CoarseVersion} from './coarse_version';
import {CustomQueryParserResultTypeMap, CustomQueryType} from './custom_query';
import {AbsoluteEntryIndex, EntriesRange} from './index_types';
@@ -28,6 +28,7 @@
private readonly entries: T[],
private readonly customQueryResult: Map<CustomQueryType, object>,
private readonly descriptors: string[],
+ private readonly noOffsets: boolean,
) {
if (timestamps.length !== entries.length) {
throw new Error(`Timestamps and entries must have the same length`);
@@ -46,10 +47,19 @@
return CoarseVersion.MOCK;
}
- getTimestamps(type: TimestampType): Timestamp[] | undefined {
- if (type !== TimestampType.REAL) {
- throw new Error('Parser mock contains only real timestamps');
- }
+ createTimestamps() {
+ throw new Error('Not implemented');
+ }
+
+ getRealToMonotonicTimeOffsetNs(): bigint | undefined {
+ return this.noOffsets ? undefined : 0n;
+ }
+
+ getRealToBootTimeOffsetNs(): bigint | undefined {
+ return this.noOffsets ? undefined : 0n;
+ }
+
+ getTimestamps(): Timestamp[] {
return this.timestamps;
}
diff --git a/tools/winscope/src/trace/screen_recording_utils.ts b/tools/winscope/src/trace/screen_recording_utils.ts
index 3427e47..e11988a 100644
--- a/tools/winscope/src/trace/screen_recording_utils.ts
+++ b/tools/winscope/src/trace/screen_recording_utils.ts
@@ -14,26 +14,18 @@
* limitations under the License.
*/
-import {Timestamp} from 'common/time';
-
-class ScreenRecordingUtils {
+export class ScreenRecordingUtils {
// Video time correction epsilon. Without correction, we could display the previous frame.
// This correction was already present in the legacy Winscope.
private static readonly EPSILON_SECONDS = 0.00001;
static timestampToVideoTimeSeconds(
- firstTimestamp: Timestamp,
- currentTimestamp: Timestamp,
+ firstTimestampNs: bigint,
+ currentTimestampNs: bigint,
) {
- if (firstTimestamp.getType() !== currentTimestamp.getType()) {
- throw new Error('Attempted to use timestamps with different type');
- }
const videoTimeSeconds =
- Number(currentTimestamp.getValueNs() - firstTimestamp.getValueNs()) /
- 1000000000 +
+ Number(currentTimestampNs - firstTimestampNs) / 1000000000 +
ScreenRecordingUtils.EPSILON_SECONDS;
return videoTimeSeconds;
}
}
-
-export {ScreenRecordingUtils};
diff --git a/tools/winscope/src/trace/trace.ts b/tools/winscope/src/trace/trace.ts
index 22610bd..43c0daf 100644
--- a/tools/winscope/src/trace/trace.ts
+++ b/tools/winscope/src/trace/trace.ts
@@ -15,7 +15,7 @@
*/
import {ArrayUtils} from 'common/array_utils';
-import {Timestamp, TimestampType} from 'common/time';
+import {Timestamp} from 'common/time';
import {
CustomQueryParamTypeMap,
CustomQueryParserResultTypeMap,
@@ -87,7 +87,7 @@
}
override async getValue(): Promise<T> {
- return await this.parser.getEntry(this.index, this.timestamp.getType());
+ return await this.parser.getEntry(this.index);
}
}
@@ -118,21 +118,16 @@
private readonly parser: Parser<T>;
private readonly descriptors: string[];
private readonly fullTrace: Trace<T>;
- private timestampType: TimestampType;
private readonly entriesRange: EntriesRange;
private frameMap?: FrameMap;
private framesRange?: FramesRange;
- static fromParser<T>(
- parser: Parser<T>,
- timestampType: TimestampType,
- ): Trace<T> {
+ static fromParser<T>(parser: Parser<T>): Trace<T> {
return new Trace(
parser.getTraceType(),
parser,
parser.getDescriptors(),
undefined,
- timestampType,
undefined,
);
}
@@ -142,7 +137,6 @@
parser: Parser<T>,
descriptors: string[],
fullTrace: Trace<T> | undefined,
- timestampType: TimestampType,
entriesRange: EntriesRange | undefined,
) {
this.type = type;
@@ -154,20 +148,12 @@
end: parser.getLengthEntries(),
};
this.lengthEntries = this.entriesRange.end - this.entriesRange.start;
- this.timestampType = timestampType;
}
getDescriptors(): string[] {
return this.parser.getDescriptors();
}
- getTimestampType(): TimestampType {
- if (this.timestampType === undefined) {
- throw new Error('Trace no fully initialized yet!');
- }
- return this.timestampType;
- }
-
setFrameInfo(frameMap: FrameMap, framesRange: FramesRange | undefined) {
if (frameMap.lengthEntries !== this.fullTrace.lengthEntries) {
throw new Error(
@@ -238,7 +224,6 @@
}
findClosestEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
- this.checkTimestampIsCompatible(time);
if (this.lengthEntries === 0) {
return undefined;
}
@@ -272,7 +257,6 @@
}
findFirstGreaterOrEqualEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
- this.checkTimestampIsCompatible(time);
if (this.lengthEntries === 0) {
return undefined;
}
@@ -288,7 +272,7 @@
}
const entry = this.getEntry(pos - this.entriesRange.start);
- if (entry.getTimestamp() < time) {
+ if (entry.getTimestamp().getValueNs() < time.getValueNs()) {
return undefined;
}
@@ -296,7 +280,6 @@
}
findFirstGreaterEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
- this.checkTimestampIsCompatible(time);
if (this.lengthEntries === 0) {
return undefined;
}
@@ -309,7 +292,7 @@
}
const entry = this.getEntry(pos - this.entriesRange.start);
- if (entry.getTimestamp() <= time) {
+ if (entry.getTimestamp().getValueNs() <= time.getValueNs()) {
return undefined;
}
@@ -364,8 +347,6 @@
}
sliceTime(start?: Timestamp, end?: Timestamp): Trace<T> {
- this.checkTimestampIsCompatible(start);
- this.checkTimestampIsCompatible(end);
const startEntry =
start === undefined
? this.entriesRange.start
@@ -493,15 +474,9 @@
}
private getFullTraceTimestamps(): Timestamp[] {
- if (this.timestampType === undefined) {
- throw new Error('Forgot to initialize trace?');
- }
-
- const timestamps = this.parser.getTimestamps(this.timestampType);
+ const timestamps = this.parser.getTimestamps();
if (!timestamps) {
- throw new Error(
- `Timestamp type ${this.timestampType} is expected to be available`,
- );
+ throw new Error('Timestamps expected to be available');
}
return timestamps;
}
@@ -537,7 +512,6 @@
this.parser,
this.descriptors,
this.fullTrace,
- this.timestampType,
entries,
);
@@ -610,18 +584,4 @@
);
}
}
-
- private checkTimestampIsCompatible(timestamp?: Timestamp) {
- if (!timestamp) {
- return;
- }
- const timestamps = this.parser.getTimestamps(timestamp.getType());
- if (timestamps === undefined) {
- throw new Error(
- `Trace ${
- this.type
- } can't be accessed using timestamp of type ${timestamp.getType()}`,
- );
- }
- }
}
diff --git a/tools/winscope/src/trace/trace_entry_test.ts b/tools/winscope/src/trace/trace_entry_test.ts
index 86d04e6..66c7fe7 100644
--- a/tools/winscope/src/trace/trace_entry_test.ts
+++ b/tools/winscope/src/trace/trace_entry_test.ts
@@ -14,22 +14,24 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
+import {UnitTestUtils} from 'test/unit/utils';
import {Trace} from './trace';
describe('TraceEntry', () => {
let trace: Trace<string>;
beforeAll(() => {
+ jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
trace = new TraceBuilder<string>()
.setTimestamps([
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(13n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(14n),
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(15n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(11n),
+ TimestampConverterUtils.makeRealTimestamp(12n),
+ TimestampConverterUtils.makeRealTimestamp(13n),
+ TimestampConverterUtils.makeRealTimestamp(14n),
+ TimestampConverterUtils.makeRealTimestamp(15n),
])
.setEntries([
'entry-0',
@@ -61,10 +63,10 @@
it('getTimestamp()', () => {
expect(trace.getEntry(0).getTimestamp()).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
);
expect(trace.getEntry(1).getTimestamp()).toEqual(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n),
+ TimestampConverterUtils.makeRealTimestamp(11n),
);
});
diff --git a/tools/winscope/src/trace/trace_test.ts b/tools/winscope/src/trace/trace_test.ts
index d078bcc..94f8a11 100644
--- a/tools/winscope/src/trace/trace_test.ts
+++ b/tools/winscope/src/trace/trace_test.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TraceUtils} from 'test/unit/trace_utils';
import {FrameMapBuilder} from './frame_map_builder';
@@ -24,13 +24,13 @@
describe('Trace', () => {
let trace: Trace<string>;
- const time9 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(9n);
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const time11 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n);
- const time12 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n);
- const time13 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(13n);
- const time14 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(14n);
- const time15 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(15n);
+ const time9 = TimestampConverterUtils.makeRealTimestamp(9n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const time11 = TimestampConverterUtils.makeRealTimestamp(11n);
+ const time12 = TimestampConverterUtils.makeRealTimestamp(12n);
+ const time13 = TimestampConverterUtils.makeRealTimestamp(13n);
+ const time14 = TimestampConverterUtils.makeRealTimestamp(14n);
+ const time15 = TimestampConverterUtils.makeRealTimestamp(15n);
beforeAll(() => {
// Time: 10 11 12 13
@@ -936,8 +936,8 @@
]);
// time
- const time12 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n);
- const time13 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(13n);
+ const time12 = TimestampConverterUtils.makeRealTimestamp(12n);
+ const time13 = TimestampConverterUtils.makeRealTimestamp(13n);
expect(
await TraceUtils.extractEntries(trace.sliceTime(time12, time12)),
).toEqual([]);
@@ -993,8 +993,8 @@
);
// time
- const time12 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n);
- const time13 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(13n);
+ const time12 = TimestampConverterUtils.makeRealTimestamp(12n);
+ const time13 = TimestampConverterUtils.makeRealTimestamp(13n);
expect(await TraceUtils.extractEntries(empty.sliceTime())).toEqual([]);
expect(await TraceUtils.extractEntries(empty.sliceTime(time12))).toEqual(
[],
diff --git a/tools/winscope/src/trace/traces_test.ts b/tools/winscope/src/trace/traces_test.ts
index e79824b..0bae84d 100644
--- a/tools/winscope/src/trace/traces_test.ts
+++ b/tools/winscope/src/trace/traces_test.ts
@@ -16,7 +16,7 @@
import {assertDefined} from 'common/assert_utils';
import {FunctionUtils} from 'common/function_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TracesUtils} from 'test/unit/traces_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
@@ -29,16 +29,16 @@
describe('Traces', () => {
let traces: Traces;
- const time1 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1n);
- const time2 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(2n);
- const time3 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(3n);
- const time4 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(4n);
- const time5 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(5n);
- const time6 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(6n);
- const time7 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(7n);
- const time8 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(8n);
- const time9 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(9n);
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
+ const time1 = TimestampConverterUtils.makeRealTimestamp(1n);
+ const time2 = TimestampConverterUtils.makeRealTimestamp(2n);
+ const time3 = TimestampConverterUtils.makeRealTimestamp(3n);
+ const time4 = TimestampConverterUtils.makeRealTimestamp(4n);
+ const time5 = TimestampConverterUtils.makeRealTimestamp(5n);
+ const time6 = TimestampConverterUtils.makeRealTimestamp(6n);
+ const time7 = TimestampConverterUtils.makeRealTimestamp(7n);
+ const time8 = TimestampConverterUtils.makeRealTimestamp(8n);
+ const time9 = TimestampConverterUtils.makeRealTimestamp(9n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
let extractedEntriesEmpty: Map<TraceType, Array<{}>>;
let extractedEntriesFull: Map<TraceType, Array<{}>>;
diff --git a/tools/winscope/src/trace/tree_node/formatters.ts b/tools/winscope/src/trace/tree_node/formatters.ts
index aa4ffd8..2e323f1 100644
--- a/tools/winscope/src/trace/tree_node/formatters.ts
+++ b/tools/winscope/src/trace/tree_node/formatters.ts
@@ -15,7 +15,7 @@
*/
import {Timestamp} from 'common/time';
-import {TimestampUtils} from 'common/timestamp_utils';
+import {TimeDuration} from 'common/time_duration';
import {RawDataUtils} from 'parsers/raw_data_utils';
import {TransformUtils} from 'parsers/surface_flinger/transform_utils';
import {PropertyTreeNode} from './property_tree_node';
@@ -210,16 +210,16 @@
}
}
-class TimestampFormatter implements PropertyFormatter {
+class TimestampNodeFormatter implements PropertyFormatter {
format(node: PropertyTreeNode): string {
const timestamp = node.getValue();
- if (timestamp instanceof Timestamp) {
- return TimestampUtils.format(timestamp);
+ if (timestamp instanceof Timestamp || timestamp instanceof TimeDuration) {
+ return timestamp.format();
}
return 'null';
}
}
-const TIMESTAMP_FORMATTER = new TimestampFormatter();
+const TIMESTAMP_NODE_FORMATTER = new TimestampNodeFormatter();
export {
EMPTY_OBJ_STRING,
@@ -236,6 +236,6 @@
REGION_FORMATTER,
EnumFormatter,
FixedStringFormatter,
- TIMESTAMP_FORMATTER,
+ TIMESTAMP_NODE_FORMATTER,
MATRIX_FORMATTER,
};
diff --git a/tools/winscope/src/trace/tree_node/property_tree_node_factory.ts b/tools/winscope/src/trace/tree_node/property_tree_node_factory.ts
index 0997af3..9ab6fdb 100644
--- a/tools/winscope/src/trace/tree_node/property_tree_node_factory.ts
+++ b/tools/winscope/src/trace/tree_node/property_tree_node_factory.ts
@@ -15,6 +15,7 @@
*/
import {Timestamp} from 'common/time';
+import {TimeDuration} from 'common/time_duration';
import {
PropertySource,
PropertyTreeNode,
@@ -115,6 +116,7 @@
if (Array.isArray(value)) return value.length > 0;
if (this.isLongType(value)) return false;
if (value instanceof Timestamp) return false;
+ if (value instanceof TimeDuration) return false;
return typeof value === 'object' && Object.keys(value).length > 0;
}
diff --git a/tools/winscope/src/trace_processor/engine.ts b/tools/winscope/src/trace_processor/engine.ts
index c6454a2..aae959f 100644
--- a/tools/winscope/src/trace_processor/engine.ts
+++ b/tools/winscope/src/trace_processor/engine.ts
@@ -15,7 +15,6 @@
import {defer, Deferred} from './deferred';
import {assertExists, assertTrue} from './logging';
import {perfetto} from '../../deps_build/trace_processor/ui/tsc/gen/protos';
-
import {ProtoRingBuffer} from './proto_ring_buffer';
import {
ComputeMetricArgs,
@@ -31,7 +30,6 @@
QueryResult,
WritableQueryResult,
} from './query_result';
-
import TraceProcessorRpc = perfetto.protos.TraceProcessorRpc;
import TraceProcessorRpcStream = perfetto.protos.TraceProcessorRpcStream;
import TPM = perfetto.protos.TraceProcessorRpc.TraceProcessorMethod;
diff --git a/tools/winscope/src/trace_processor/query_result.ts b/tools/winscope/src/trace_processor/query_result.ts
index 4daa72c..24dd2e8 100644
--- a/tools/winscope/src/trace_processor/query_result.ts
+++ b/tools/winscope/src/trace_processor/query_result.ts
@@ -52,9 +52,7 @@
// The Winscope parsers need the 64-bit proto fields to be retrieved as Long instead of number,
// otherwise data (e.g. state flags) would be lost because of the 53-bit integer limitation.
// import './static_initializers';
-
import protobuf from 'protobufjs/minimal';
-
import {defer, Deferred} from './deferred';
import {assertExists, assertFalse, assertTrue} from './logging';
import {utf8Decode} from './string_utils';
diff --git a/tools/winscope/src/trace_processor/string_utils.ts b/tools/winscope/src/trace_processor/string_utils.ts
index bc33b70..7f7a859 100644
--- a/tools/winscope/src/trace_processor/string_utils.ts
+++ b/tools/winscope/src/trace_processor/string_utils.ts
@@ -22,7 +22,6 @@
read as utf8Read,
write as utf8Write,
} from '@protobufjs/utf8';
-
import {assertTrue} from './logging';
// TextDecoder/Decoder requires the full DOM and isn't available in all types
diff --git a/tools/winscope/src/trace_processor/wasm_engine_proxy.ts b/tools/winscope/src/trace_processor/wasm_engine_proxy.ts
index 2cf01f6..9ea76d5 100644
--- a/tools/winscope/src/trace_processor/wasm_engine_proxy.ts
+++ b/tools/winscope/src/trace_processor/wasm_engine_proxy.ts
@@ -13,7 +13,6 @@
// limitations under the License.
import {assertExists, assertTrue} from './logging';
-
import {Engine, LoadingTracker} from './engine';
let bundlePath: string;
diff --git a/tools/winscope/src/viewers/common/ime_utils.ts b/tools/winscope/src/viewers/common/ime_utils.ts
index 91d2dd1..242b43e 100644
--- a/tools/winscope/src/viewers/common/ime_utils.ts
+++ b/tools/winscope/src/viewers/common/ime_utils.ts
@@ -15,7 +15,6 @@
*/
import {assertDefined} from 'common/assert_utils';
import {Timestamp} from 'common/time';
-import {TimestampUtils} from 'common/timestamp_utils';
import {Item} from 'trace/item';
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
@@ -86,9 +85,7 @@
const displayContent = entry.getAllChildren()[0];
const props: WmStateProperties = {
- timestamp: wmEntryTimestamp
- ? TimestampUtils.format(wmEntryTimestamp)
- : undefined,
+ timestamp: wmEntryTimestamp ? wmEntryTimestamp.format() : undefined,
focusedApp: entry.getEagerPropertyByName('focusedApp')?.getValue(),
focusedWindow: this.getFocusedWindowString(entry),
focusedActivity: this.getFocusedActivityString(entry),
@@ -171,7 +168,7 @@
);
const rootProperties = sfEntryTimestamp
- ? {timestamp: TimestampUtils.format(sfEntryTimestamp)}
+ ? {timestamp: sfEntryTimestamp.format()}
: undefined;
return new ImeLayers(
diff --git a/tools/winscope/src/viewers/common/presenter_input_method.ts b/tools/winscope/src/viewers/common/presenter_input_method.ts
index 57192d7..e561766 100644
--- a/tools/winscope/src/viewers/common/presenter_input_method.ts
+++ b/tools/winscope/src/viewers/common/presenter_input_method.ts
@@ -17,7 +17,6 @@
import {assertDefined} from 'common/assert_utils';
import {PersistentStoreProxy} from 'common/persistent_store_proxy';
import {Timestamp} from 'common/time';
-import {TimestampUtils} from 'common/timestamp_utils';
import {WinscopeEvent, WinscopeEventType} from 'messaging/winscope_event';
import {Trace, TraceEntry} from 'trace/trace';
import {Traces} from 'trace/traces';
@@ -404,9 +403,7 @@
return [undefined, undefined, undefined];
}
- this.currentImeEntryTimestamp = TimestampUtils.format(
- imeEntry.getTimestamp(),
- );
+ this.currentImeEntryTimestamp = imeEntry.getTimestamp().format();
if (!this.imeTrace.hasFrameInfo()) {
return [imeEntry, undefined, undefined];
diff --git a/tools/winscope/src/viewers/components/property_tree_node_data_view_component_test.ts b/tools/winscope/src/viewers/components/property_tree_node_data_view_component_test.ts
index 82e3580..151115f 100644
--- a/tools/winscope/src/viewers/components/property_tree_node_data_view_component_test.ts
+++ b/tools/winscope/src/viewers/components/property_tree_node_data_view_component_test.ts
@@ -21,9 +21,9 @@
import {MatButtonModule} from '@angular/material/button';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
import {ViewerEvents} from 'viewers/common/viewer_events';
@@ -59,9 +59,9 @@
.setRootId('test node')
.setName('timestamp')
.setValue(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(1659126889102158832n),
+ TimestampConverterUtils.makeRealTimestamp(1659126889102158832n),
)
- .setFormatter(TIMESTAMP_FORMATTER)
+ .setFormatter(TIMESTAMP_NODE_FORMATTER)
.build(),
);
component.node = node;
diff --git a/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts b/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts
index bb88c3f..f0b293e 100644
--- a/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_protolog/presenter_test.ts
@@ -15,9 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TraceBuilder} from 'test/unit/trace_builder';
import {Trace} from 'trace/trace';
@@ -25,7 +25,7 @@
import {TraceType} from 'trace/trace_type';
import {
DEFAULT_PROPERTY_FORMATTER,
- TIMESTAMP_FORMATTER,
+ TIMESTAMP_NODE_FORMATTER,
} from 'trace/tree_node/formatters';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {Presenter} from './presenter';
@@ -41,12 +41,12 @@
let outputUiData: undefined | UiData;
beforeEach(async () => {
- const time10 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n);
- const time11 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(11n);
- const time12 = NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(12n);
- const elapsedTime10 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n);
- const elapsedTime20 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(20n);
- const elapsedTime30 = NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(30n);
+ const time10 = TimestampConverterUtils.makeRealTimestamp(10n);
+ const time11 = TimestampConverterUtils.makeRealTimestamp(11n);
+ const time12 = TimestampConverterUtils.makeRealTimestamp(12n);
+ const elapsedTime10 = TimestampConverterUtils.makeElapsedTimestamp(10n);
+ const elapsedTime20 = TimestampConverterUtils.makeElapsedTimestamp(20n);
+ const elapsedTime30 = TimestampConverterUtils.makeElapsedTimestamp(30n);
const entries = [
new PropertyTreeBuilder()
@@ -57,7 +57,7 @@
{
name: 'timestamp',
value: elapsedTime10,
- formatter: TIMESTAMP_FORMATTER,
+ formatter: TIMESTAMP_NODE_FORMATTER,
},
{name: 'tag', value: 'tag0', formatter: DEFAULT_PROPERTY_FORMATTER},
{
@@ -81,7 +81,7 @@
{
name: 'timestamp',
value: elapsedTime20,
- formatter: TIMESTAMP_FORMATTER,
+ formatter: TIMESTAMP_NODE_FORMATTER,
},
{name: 'tag', value: 'tag1', formatter: DEFAULT_PROPERTY_FORMATTER},
{
@@ -105,7 +105,7 @@
{
name: 'timestamp',
value: elapsedTime30,
- formatter: TIMESTAMP_FORMATTER,
+ formatter: TIMESTAMP_NODE_FORMATTER,
},
{name: 'tag', value: 'tag2', formatter: DEFAULT_PROPERTY_FORMATTER},
{
diff --git a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
index 2636ec7..55f65cc 100644
--- a/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_protolog/viewer_protolog_component_test.ts
@@ -29,9 +29,9 @@
import {MatSelectModule} from '@angular/material/select';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {executeScrollComponentTests} from 'viewers/common/scroll_component_test_utils';
import {ViewerEvents} from 'viewers/common/viewer_events';
import {SelectWithFilterComponent} from 'viewers/components/select_with_filter_component';
@@ -271,8 +271,8 @@
const time = new PropertyTreeBuilder()
.setRootId('ProtologMessage')
.setName('timestamp')
- .setValue(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(10n))
- .setFormatter(TIMESTAMP_FORMATTER)
+ .setValue(TimestampConverterUtils.makeElapsedTimestamp(10n))
+ .setFormatter(TIMESTAMP_NODE_FORMATTER)
.build();
const messages = [];
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts b/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts
index 83aefe3..418f7d7 100644
--- a/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/presenter.ts
@@ -16,7 +16,6 @@
import {assertDefined} from 'common/assert_utils';
import {PersistentStoreProxy} from 'common/persistent_store_proxy';
-import {TimestampUtils} from 'common/timestamp_utils';
import {WinscopeEvent, WinscopeEventType} from 'messaging/winscope_event';
import {LayerFlag} from 'parsers/surface_flinger/layer_flag';
import {Trace, TraceEntry} from 'trace/trace';
@@ -144,9 +143,7 @@
);
this.currentHierarchyTree = await entry?.getValue();
if (entry) {
- this.currentHierarchyTreeName = TimestampUtils.format(
- entry.getTimestamp(),
- );
+ this.currentHierarchyTreeName = entry.getTimestamp().format();
}
this.previousEntry =
diff --git a/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts b/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts
index 7382ea4..39c4e04 100644
--- a/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_surface_flinger/presenter_test.ts
@@ -15,9 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {MockStorage} from 'test/unit/mock_storage';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TreeNodeUtils} from 'test/unit/tree_node_utils';
import {UnitTestUtils} from 'test/unit/utils';
@@ -85,7 +85,7 @@
const presenter = createPresenter(emptyTrace);
const positionUpdateWithoutTraceEntry = TracePositionUpdate.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
+ TimestampConverterUtils.makeRealTimestamp(0n),
);
await presenter.onAppEvent(positionUpdateWithoutTraceEntry);
expect(uiData.hierarchyUserOptions).toBeTruthy();
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter.ts b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
index 64badca..c9a538b 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter.ts
@@ -22,7 +22,7 @@
import {Traces} from 'trace/traces';
import {TraceEntryFinder} from 'trace/trace_entry_finder';
import {TraceType} from 'trace/trace_type';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {DEFAULT_PROPERTY_TREE_NODE_FACTORY} from 'trace/tree_node/property_tree_node_factory';
import {Filter} from 'viewers/common/operations/filter';
@@ -406,7 +406,7 @@
'timestamp',
entry.getTimestamp(),
);
- entryTimestamp.setFormatter(TIMESTAMP_FORMATTER);
+ entryTimestamp.setFormatter(TIMESTAMP_NODE_FORMATTER);
for (const transactionState of assertDefined(
entryNode.getChildByName('transactions'),
diff --git a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
index 03b1c4f..279fd88 100644
--- a/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/presenter_test.ts
@@ -15,10 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {MockStorage} from 'test/unit/mock_storage';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TreeNodeUtils} from 'test/unit/tree_node_utils';
@@ -48,7 +47,7 @@
beforeEach(async () => {
outputUiData = undefined;
- await setUpTestEnvironment(TimestampType.ELAPSED);
+ await setUpTestEnvironment();
});
it('is robust to empty trace', async () => {
@@ -75,7 +74,7 @@
};
await presenter.onAppEvent(
TracePositionUpdate.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
),
);
expect(outputUiData).toEqual(UiData.EMPTY);
@@ -342,25 +341,15 @@
expect(assertDefined(outputUiData).currentEntryIndex).toEqual(13);
});
- it('formats real time', async () => {
- await setUpTestEnvironment(TimestampType.REAL);
+ it('formats entry time', async () => {
+ await setUpTestEnvironment();
expect(
assertDefined(outputUiData).entries[0].time.formattedValue(),
).toEqual('2022-08-03T06:19:01.051480997');
});
- it('formats elapsed time', async () => {
- await setUpTestEnvironment(TimestampType.ELAPSED);
- expect(
- assertDefined(outputUiData).entries[0].time.formattedValue(),
- ).toEqual('2s450ms981445ns');
- });
-
- async function setUpTestEnvironment(timestampType: TimestampType) {
- trace = new TraceBuilder<PropertyTreeNode>()
- .setParser(parser)
- .setTimestampType(timestampType)
- .build();
+ async function setUpTestEnvironment() {
+ trace = new TraceBuilder<PropertyTreeNode>().setParser(parser).build();
traces = new Traces();
traces.setTrace(TraceType.TRANSACTIONS, trace);
diff --git a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
index c8d3143..b30a594 100644
--- a/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transactions/viewer_transactions_component_test.ts
@@ -30,9 +30,9 @@
import {MatSelectModule} from '@angular/material/select';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {executeScrollComponentTests} from 'viewers/common/scroll_component_test_utils';
import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
import {ViewerEvents} from 'viewers/common/viewer_events';
@@ -267,8 +267,8 @@
const time = new PropertyTreeBuilder()
.setRootId(propertiesTree.id)
.setName('timestamp')
- .setValue(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1n))
- .setFormatter(TIMESTAMP_FORMATTER)
+ .setValue(TimestampConverterUtils.makeElapsedTimestamp(1n))
+ .setFormatter(TIMESTAMP_NODE_FORMATTER)
.build();
const entry = new UiDataEntry(
@@ -352,8 +352,8 @@
const time = new PropertyTreeBuilder()
.setRootId(propertiesTree.id)
.setName('timestamp')
- .setValue(NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(1n))
- .setFormatter(TIMESTAMP_FORMATTER)
+ .setValue(TimestampConverterUtils.makeElapsedTimestamp(1n))
+ .setFormatter(TIMESTAMP_NODE_FORMATTER)
.build();
const uiData = new UiData(
diff --git a/tools/winscope/src/viewers/viewer_transitions/presenter_test.ts b/tools/winscope/src/viewers/viewer_transitions/presenter_test.ts
index 41a4461..257fd8e 100644
--- a/tools/winscope/src/viewers/viewer_transitions/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/presenter_test.ts
@@ -15,9 +15,8 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TracesBuilder} from 'test/unit/traces_builder';
import {TraceBuilder} from 'test/unit/trace_builder';
import {UnitTestUtils} from 'test/unit/utils';
@@ -39,7 +38,7 @@
await presenter.onAppEvent(
TracePositionUpdate.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(10n),
+ TimestampConverterUtils.makeRealTimestamp(10n),
),
);
expect(outputUiData).toEqual(UiData.EMPTY);
@@ -53,7 +52,6 @@
const trace = new TraceBuilder<PropertyTreeNode>()
.setParser(parser)
- .setTimestampType(TimestampType.REAL)
.build();
const traces = new Traces();
@@ -74,7 +72,7 @@
expect(wmData.getChildByName('id')?.formattedValue()).toEqual('32');
expect(wmData.getChildByName('type')?.formattedValue()).toEqual('OPEN');
expect(wmData.getChildByName('createTimeNs')?.formattedValue()).toEqual(
- '2023-11-21T13:38:23.083364560',
+ '2023-11-21T13:30:25.428925648',
);
});
});
diff --git a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
index aa58118..cea2ff6 100644
--- a/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
+++ b/tools/winscope/src/viewers/viewer_transitions/viewer_transitions_component_test.ts
@@ -23,10 +23,9 @@
import {MatDividerModule} from '@angular/material/divider';
import {MatIconModule} from '@angular/material/icon';
import {assertDefined} from 'common/assert_utils';
-import {TimestampType} from 'common/time';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {UnitTestUtils} from 'test/unit/utils';
import {Parser} from 'trace/parser';
import {Trace} from 'trace/trace';
@@ -34,7 +33,7 @@
import {TracePosition} from 'trace/trace_position';
import {TraceType} from 'trace/trace_type';
import {Transition} from 'trace/transition';
-import {TIMESTAMP_FORMATTER} from 'trace/tree_node/formatters';
+import {TIMESTAMP_NODE_FORMATTER} from 'trace/tree_node/formatters';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {ViewerEvents} from 'viewers/common/viewer_events';
import {PropertiesComponent} from 'viewers/components/properties_component';
@@ -141,7 +140,7 @@
'traces/elapsed_and_real_timestamp/wm_transition_trace.pb',
'traces/elapsed_and_real_timestamp/shell_transition_trace.pb',
])) as Parser<PropertyTreeNode>;
- const trace = Trace.fromParser(parser, TimestampType.REAL);
+ const trace = Trace.fromParser(parser);
const traces = new Traces();
traces.setTrace(TraceType.TRANSITION, trace);
@@ -233,9 +232,9 @@
.setRootId(transitionTree.id)
.setName('sendTimeNs')
.setValue(
- NO_TIMEZONE_OFFSET_FACTORY.makeElapsedTimestamp(BigInt(sendTimeNanos)),
+ TimestampConverterUtils.makeElapsedTimestamp(BigInt(sendTimeNanos)),
)
- .setFormatter(TIMESTAMP_FORMATTER)
+ .setFormatter(TIMESTAMP_NODE_FORMATTER)
.build();
return {
diff --git a/tools/winscope/src/viewers/viewer_view_capture/presenter_test.ts b/tools/winscope/src/viewers/viewer_view_capture/presenter_test.ts
index 1d757a6..1158bfe 100644
--- a/tools/winscope/src/viewers/viewer_view_capture/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_view_capture/presenter_test.ts
@@ -15,9 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {MockStorage} from 'test/unit/mock_storage';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TreeNodeUtils} from 'test/unit/tree_node_utils';
import {UnitTestUtils} from 'test/unit/utils';
@@ -88,7 +88,7 @@
const presenter = createPresenter(emptyTrace);
const positionUpdateWithoutTraceEntry = TracePositionUpdate.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
+ TimestampConverterUtils.makeRealTimestamp(0n),
);
await presenter.onAppEvent(positionUpdateWithoutTraceEntry);
expect(uiData.hierarchyUserOptions).toBeTruthy();
diff --git a/tools/winscope/src/viewers/viewer_window_manager/presenter.ts b/tools/winscope/src/viewers/viewer_window_manager/presenter.ts
index 8b2e747..ae1c155 100644
--- a/tools/winscope/src/viewers/viewer_window_manager/presenter.ts
+++ b/tools/winscope/src/viewers/viewer_window_manager/presenter.ts
@@ -16,7 +16,6 @@
import {assertDefined} from 'common/assert_utils';
import {PersistentStoreProxy} from 'common/persistent_store_proxy';
-import {TimestampUtils} from 'common/timestamp_utils';
import {WinscopeEvent, WinscopeEventType} from 'messaging/winscope_event';
import {Trace, TraceEntry} from 'trace/trace';
import {Traces} from 'trace/traces';
@@ -131,9 +130,7 @@
);
this.currentHierarchyTree = await entry?.getValue();
if (entry) {
- this.currentHierarchyTreeName = TimestampUtils.format(
- entry.getTimestamp(),
- );
+ this.currentHierarchyTreeName = entry.getTimestamp().format();
}
this.previousEntry =
diff --git a/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts b/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts
index 706c351..7ad5e3c 100644
--- a/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts
+++ b/tools/winscope/src/viewers/viewer_window_manager/presenter_test.ts
@@ -15,9 +15,9 @@
*/
import {assertDefined} from 'common/assert_utils';
-import {NO_TIMEZONE_OFFSET_FACTORY} from 'common/timestamp_factory';
import {TracePositionUpdate} from 'messaging/winscope_event';
import {MockStorage} from 'test/unit/mock_storage';
+import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
import {TraceBuilder} from 'test/unit/trace_builder';
import {TreeNodeUtils} from 'test/unit/tree_node_utils';
import {UnitTestUtils} from 'test/unit/utils';
@@ -75,7 +75,7 @@
expect(uiData.tree).toBeFalsy();
const positionUpdateWithoutTraceEntry = TracePositionUpdate.fromTimestamp(
- NO_TIMEZONE_OFFSET_FACTORY.makeRealTimestamp(0n),
+ TimestampConverterUtils.makeRealTimestamp(0n),
);
await presenter.onAppEvent(positionUpdateWithoutTraceEntry);
expect(uiData.hierarchyUserOptions).toBeTruthy();