blob: 933ce45c7e62d2ea7f8a5cda5c72c093a9bbcee8 [file] [log] [blame]
Steve Golton6e00af92023-09-04 11:03:30 +01001// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import m from 'mithril';
16
Hector Dearmande1dd012023-12-12 15:10:00 +000017import {Disposable} from '../base/disposable';
18import {duration, Time, time, TimeSpan} from '../base/time';
Steve Golton0b26ef62023-09-07 15:23:55 +010019import {raf} from '../core/raf_scheduler';
Steve Golton6e00af92023-09-04 11:03:30 +010020import {globals} from '../frontend/globals';
Steve Golton57a9d4e2023-12-12 08:43:38 +000021import {PanelSize} from '../frontend/panel';
Steve Goltonad72b582023-11-23 15:48:55 +000022import {SliceRect, Track, TrackContext} from '../public';
Steve Golton6e00af92023-09-04 11:03:30 +010023
Steve Golton6e00af92023-09-04 11:03:30 +010024export {Store} from '../frontend/store';
Hector Dearmanea4719d2023-10-31 09:11:09 +000025export {EngineProxy} from '../trace_processor/engine';
Steve Golton6e00af92023-09-04 11:03:30 +010026export {
27 LONG,
28 LONG_NULL,
29 NUM,
30 NUM_NULL,
31 STR,
32 STR_NULL,
Hector Dearmanea4719d2023-10-31 09:11:09 +000033} from '../trace_processor/query_result';
Steve Golton6e00af92023-09-04 11:03:30 +010034
Hector Dearmande1dd012023-12-12 15:10:00 +000035type FetchTimeline<Data> = (start: time, end: time, resolution: duration) =>
36 Promise<Data>;
37
38// This helper provides the logic to call |doFetch()| only when more
39// data is needed as the visible window is panned and zoomed about, and
40// includes an FSM to ensure doFetch is not re-entered.
41class TimelineFetcher<Data> implements Disposable {
42 private requestingData = false;
43 private queuedRequest = false;
44 private doFetch: FetchTimeline<Data>;
45
46 private data_?: Data;
47
48 // Timespan and resolution of the latest *request*. data_ may cover
49 // a different time window.
50 private latestTimespan: TimeSpan;
51 private latestResolution: duration;
52
53 constructor(doFetch: FetchTimeline<Data>) {
54 this.doFetch = doFetch;
55 this.latestTimespan = TimeSpan.ZERO;
56 this.latestResolution = 0n;
57 }
58
59 requestDataForCurrentTime(): void {
60 const currentTimeSpan = globals.frontendLocalState.visibleTimeSpan;
61 const currentResolution = globals.getCurResolution();
62 this.requestData(currentTimeSpan, currentResolution);
63 }
64
65 requestData(timespan: TimeSpan, resolution: duration): void {
66 if (this.shouldLoadNewData(timespan, resolution)) {
67 // Over request data, one page worth to the left and right.
68 const start = Time.sub(timespan.start, timespan.duration);
69 const end = Time.add(timespan.end, timespan.duration);
70 this.latestTimespan = new TimeSpan(start, end);
71 this.latestResolution = resolution;
72 this.loadData();
73 }
74 }
75
76 get data(): Data|undefined {
77 return this.data_;
78 }
79
80 dispose() {
81 this.queuedRequest = false;
82 this.data_ = undefined;
83 }
84
85 private shouldLoadNewData(timespan: TimeSpan, resolution: duration): boolean {
86 if (this.data_ === undefined) {
87 return true;
88 }
89
90 if (timespan.start < this.latestTimespan.start) {
91 return true;
92 }
93
94 if (timespan.end > this.latestTimespan.end) {
95 return true;
96 }
97
98 if (resolution !== this.latestResolution) {
99 return true;
100 }
101
102 return false;
103 }
104
105 private loadData(): void {
106 if (this.requestingData) {
107 this.queuedRequest = true;
108 return;
109 }
110 const {start, end} = this.latestTimespan;
111 const resolution = this.latestResolution;
112 this.doFetch(start, end, resolution).then((data) => {
113 this.requestingData = false;
114 this.data_ = data;
115 if (this.queuedRequest) {
116 this.queuedRequest = false;
117 this.loadData();
118 } else {
119 raf.scheduleRedraw();
120 }
121 });
122 this.requestingData = true;
123 }
124}
125
Steve Golton4c924692023-12-04 10:16:00 +0000126// A helper class which provides a base track implementation for tracks which
127// load their content asynchronously from the trace.
128//
129// Tracks extending this base class need only define |renderCanvas()| and
130// |onBoundsChange()|. This helper provides sensible default implementations for
131// all the |Track| interface methods which subclasses may also choose to
132// override if necessary.
133//
134// This helper provides the logic to call |onBoundsChange()| only when more data
135// is needed as the visible window is panned and zoomed about, and includes an
136// FSM to ensure onBoundsChange is not re-entered, and that the track doesn't
137// render stale data.
138//
139// Note: This class is deprecated and should not be used for new tracks. Use
140// |BaseSliceTrack| instead.
141export abstract class TrackHelperLEGACY<Data> implements Track {
Hector Dearmande1dd012023-12-12 15:10:00 +0000142 private timelineFetcher: TimelineFetcher<Data>;
143
144 constructor() {
145 this.timelineFetcher =
146 new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
147 }
Steve Golton6e00af92023-09-04 11:03:30 +0100148
Steve Goltond29ef3c2023-10-26 12:36:54 +0100149 onCreate(_ctx: TrackContext): void {}
Steve Goltonde040642023-09-25 12:57:29 +0100150
Steve Goltonb752fbd2023-10-05 12:57:40 +0100151 onDestroy(): void {
Hector Dearmande1dd012023-12-12 15:10:00 +0000152 this.timelineFetcher.dispose();
153 }
154
155 get data(): Data|undefined {
156 return this.timelineFetcher.data;
Steve Golton6e00af92023-09-04 11:03:30 +0100157 }
158
159 // Returns a place where a given slice should be drawn. Should be implemented
160 // only for track types that support slices e.g. chrome_slice, async_slices
161 // tStart - slice start time in seconds, tEnd - slice end time in seconds,
162 // depth - slice depth
Steve Goltonab40feb2023-12-08 12:42:32 +0000163 getSliceRect(_tStart: time, _tEnd: time, _depth: number): SliceRect
164 |undefined {
Steve Golton6e00af92023-09-04 11:03:30 +0100165 return undefined;
166 }
167
168 abstract getHeight(): number;
169
Alexander Timin9ff766e2023-10-12 14:18:10 -0700170 getTrackShellButtons(): m.Children {
Steve Golton6e00af92023-09-04 11:03:30 +0100171 return [];
172 }
173
Steve Golton6e00af92023-09-04 11:03:30 +0100174 onMouseMove(_position: {x: number; y: number;}): void {}
175
176 onMouseClick(_position: {x: number; y: number;}): boolean {
177 return false;
178 }
179
180 onMouseOut(): void {}
181
182 onFullRedraw(): void {}
183
184 abstract onBoundsChange(start: time, end: time, resolution: duration):
185 Promise<Data>;
186
Steve Golton57a9d4e2023-12-12 08:43:38 +0000187 abstract renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize): void;
Steve Golton6e00af92023-09-04 11:03:30 +0100188
Steve Golton57a9d4e2023-12-12 08:43:38 +0000189 render(ctx: CanvasRenderingContext2D, size: PanelSize): void {
Hector Dearmande1dd012023-12-12 15:10:00 +0000190 this.timelineFetcher.requestDataForCurrentTime();
Steve Golton57a9d4e2023-12-12 08:43:38 +0000191 this.renderCanvas(ctx, size);
Steve Golton6e00af92023-09-04 11:03:30 +0100192 }
Steve Golton6e00af92023-09-04 11:03:30 +0100193}