aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Golton <stevegolton@google.com>2024-05-08 17:17:52 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-05-08 17:17:52 +0000
commitfa413e8c9b44535bd3a97d65069628e22eb21437 (patch)
tree889f872cff80e8e38f53ae1364a588b35ad02c8d
parenteca31d752567b4c1655edd87d6577cbfb7967ebf (diff)
parent53b648b0d2e149375a779124b2bcf6dc9e310028 (diff)
downloadperfetto-fa413e8c9b44535bd3a97d65069628e22eb21437.tar.gz
Merge "ui: Remove the last of the "V1" slice tracks" into main
-rw-r--r--test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha2562
-rw-r--r--test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha2562
-rw-r--r--test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha2562
-rw-r--r--ui/src/common/actions_unittest.ts2
-rw-r--r--ui/src/controller/aggregation/slice_aggregation_controller.ts2
-rw-r--r--ui/src/controller/flow_events_controller.ts2
-rw-r--r--ui/src/controller/selection_controller.ts2
-rw-r--r--ui/src/controller/track_decider.ts2
-rw-r--r--ui/src/frontend/chrome_slice_details_tab.ts2
-rw-r--r--ui/src/frontend/named_slice_track.ts3
-rw-r--r--ui/src/frontend/slice_track.ts404
-rw-r--r--ui/src/tracks/annotation/index.ts15
-rw-r--r--ui/src/tracks/chrome_slices/chrome_slice_track.ts109
-rw-r--r--ui/src/tracks/chrome_slices/generic_slice_track.ts30
-rw-r--r--ui/src/tracks/chrome_slices/index.ts202
-rw-r--r--ui/src/tracks/visualised_args/index.ts98
-rw-r--r--ui/src/tracks/visualised_args/visualized_args_track.ts93
17 files changed, 234 insertions, 738 deletions
diff --git a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
index 81c36d88e..ea62427a6 100644
--- a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
@@ -1 +1 @@
-ff45b08442a6f59a4768f3ad697cffb43d19889f1f6523d5ed4ef83aff944864 \ No newline at end of file
+8b878dd1d284fda4558de3f184d078293546c09a1438e14183161eb6040cee26 \ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
index 4d7b25a06..e6a988a37 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
@@ -1 +1 @@
-161c05f6bae9b0e2142e73e83f3bcf7a9c689c58411db9e345a63ba882605557 \ No newline at end of file
+d3e7cdfbb83dd4e1b674c5d7b90554fd915dab8747f3981a2b6dbb1fff955f61 \ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
index 2d4876158..1be3a540d 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
@@ -1 +1 @@
-c0bf63de1c6c738fb994d64e5f2f7c5c0e4315f8627fc5fd68f5b906acbb814d \ No newline at end of file
+e1d34e03cd7540d476eb93e159a785ea18a80083b3410898a967a56f9d7af5f8 \ No newline at end of file
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index b31d2ae9c..078440292 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -17,7 +17,6 @@ import {produce} from 'immer';
import {assertExists} from '../base/logging';
import {Time} from '../base/time';
import {PrimaryTrackSortKey} from '../public';
-import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
import {HEAP_PROFILE_TRACK_KIND} from '../tracks/heap_profile';
import {PROCESS_SCHEDULING_TRACK_KIND} from '../tracks/process_summary/process_scheduling_track';
import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state';
@@ -32,6 +31,7 @@ import {
TraceUrlSource,
TrackSortKey,
} from './state';
+import {SLICE_TRACK_KIND} from '../tracks/chrome_slices/chrome_slice_track';
function fakeTrack(
state: State,
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index 62085946c..3e18dd040 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -17,7 +17,7 @@ import {Area, Sorting} from '../../common/state';
import {globals} from '../../frontend/globals';
import {Engine} from '../../trace_processor/engine';
import {ASYNC_SLICE_TRACK_KIND} from '../../tracks/async_slices';
-import {SLICE_TRACK_KIND} from '../../tracks/chrome_slices';
+import {SLICE_TRACK_KIND} from '../../tracks/chrome_slices/chrome_slice_track';
import {AggregationController} from './aggregation_controller';
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index d53142c81..183786287 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -20,7 +20,7 @@ import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish';
import {asSliceSqlId} from '../frontend/sql_types';
import {Engine} from '../trace_processor/engine';
import {LONG, NUM, STR_NULL} from '../trace_processor/query_result';
-import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
+import {SLICE_TRACK_KIND} from '../tracks/chrome_slices/chrome_slice_track';
import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../tracks/frames';
import {Controller} from './controller';
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index d30b458b3..ce9b15f18 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -37,7 +37,7 @@ import {
STR_NULL,
timeFromSql,
} from '../trace_processor/query_result';
-import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
+import {SLICE_TRACK_KIND} from '../tracks/chrome_slices/chrome_slice_track';
import {Controller} from './controller';
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 16c6453ac..58f77c003 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -35,7 +35,6 @@ import {
getScrollJankTracks,
} from '../tracks/chrome_scroll_jank';
import {decideTracks as scrollJankDecideTracks} from '../tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track';
-import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
import {COUNTER_TRACK_KIND} from '../tracks/counter';
import {
ACTUAL_FRAMES_SLICE_TRACK_KIND,
@@ -43,6 +42,7 @@ import {
} from '../tracks/frames';
import {decideTracks as screenshotDecideTracks} from '../tracks/screenshots';
import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state';
+import {SLICE_TRACK_KIND} from '../tracks/chrome_slices/chrome_slice_track';
const MEM_DMA_COUNTER_NAME = 'mem.dma_heap';
const MEM_DMA = 'mem.dma_buffer';
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index f42d4b60a..49196fa21 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -222,7 +222,7 @@ async function getSliceDetails(
id: number,
table: string,
): Promise<SliceDetails | undefined> {
- if (table === 'annotation') {
+ if (table === 'annotation_slice') {
return getAnnotationSlice(engine, id);
} else {
return getSlice(engine, asSliceSqlId(id));
diff --git a/ui/src/frontend/named_slice_track.ts b/ui/src/frontend/named_slice_track.ts
index 5eff6af74..9d0eb75f7 100644
--- a/ui/src/frontend/named_slice_track.ts
+++ b/ui/src/frontend/named_slice_track.ts
@@ -81,9 +81,6 @@ export abstract class NamedSliceTrack<
kind: 'CHROME_SLICE',
id: args.slice.id,
trackKey: this.trackKey,
- // |table| here can be either 'slice' or 'annotation'. The
- // AnnotationSliceTrack overrides the onSliceClick and sets this to
- // 'annotation'
table: 'slice',
},
{
diff --git a/ui/src/frontend/slice_track.ts b/ui/src/frontend/slice_track.ts
deleted file mode 100644
index e12cd7377..000000000
--- a/ui/src/frontend/slice_track.ts
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {duration, Time, time} from '../base/time';
-import {Actions} from '../common/actions';
-import {cropText, drawIncompleteSlice} from '../common/canvas_utils';
-import {getColorForSlice} from '../core/colorizer';
-import {HighPrecisionTime} from '../common/high_precision_time';
-import {TrackData} from '../common/track_data';
-import {TimelineFetcher} from '../common/track_helper';
-import {SliceRect, Track} from '../public';
-import {getLegacySelection} from '../common/state';
-
-import {CROP_INCOMPLETE_SLICE_FLAG} from './base_slice_track';
-import {checkerboardExcept} from './checkerboard';
-import {globals} from './globals';
-import {PanelSize} from './panel';
-
-export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
-const SLICE_HEIGHT = 18;
-const TRACK_PADDING = 2;
-const CHEVRON_WIDTH_PX = 10;
-const HALF_CHEVRON_WIDTH_PX = CHEVRON_WIDTH_PX / 2;
-const INCOMPLETE_SLICE_WIDTH_PX = 20;
-
-export interface SliceData extends TrackData {
- // Slices are stored in a columnar fashion.
- strings: string[];
- sliceIds: Float64Array;
- starts: BigInt64Array;
- ends: BigInt64Array;
- depths: Uint16Array;
- titles: Uint16Array; // Index into strings.
- colors?: Uint16Array; // Index into strings.
- isInstant: Uint16Array;
- isIncomplete: Uint16Array;
- cpuTimeRatio?: Float64Array;
-}
-
-// Track base class which handles rendering slices in a generic way.
-// This is the old way of rendering slices - i.e. "track v1" format - and
-// exists as a patch to allow old tracks to be converted to controller-less
-// tracks before they are ported to v2.
-// Slice tracks should extend this class and implement the abstract methods,
-// notably onBoundsChange().
-// Note: This class is deprecated and should not be used for new tracks. Use
-// |BaseSliceTrack| instead.
-export abstract class SliceTrackLEGACY implements Track {
- private fetcher = new TimelineFetcher(this.onBoundsChange.bind(this));
-
- constructor(
- private maxDepth: number,
- protected trackKey: string,
- private tableName: string,
- private namespace?: string,
- ) {}
-
- async onUpdate(): Promise<void> {
- await this.fetcher.requestDataForCurrentTime();
- }
-
- async onDestroy(): Promise<void> {
- this.fetcher.dispose();
- }
-
- abstract onBoundsChange(
- start: time,
- end: time,
- resolution: duration,
- ): Promise<SliceData>;
-
- protected namespaceTable(tableName: string = this.tableName): string {
- if (this.namespace) {
- return this.namespace + '_' + tableName;
- } else {
- return tableName;
- }
- }
-
- private hoveredTitleId = -1;
-
- // Font used to render the slice name on the current track.
- protected getFont() {
- return '12px Roboto Condensed';
- }
-
- render(ctx: CanvasRenderingContext2D, size: PanelSize): void {
- // TODO: fonts and colors should come from the CSS and not hardcoded here.
- const data = this.fetcher.data;
- if (data === undefined) return; // Can't possibly draw anything.
-
- const {visibleTimeSpan, visibleTimeScale} = globals.timeline;
-
- // If the cached trace slices don't fully cover the visible time range,
- // show a gray rectangle with a "Loading..." label.
- checkerboardExcept(
- ctx,
- this.getHeight(),
- 0,
- size.width,
- visibleTimeScale.timeToPx(data.start),
- visibleTimeScale.timeToPx(data.end),
- );
-
- ctx.textAlign = 'center';
-
- // measuretext is expensive so we only use it once.
- const charWidth = ctx.measureText('ACBDLqsdfg').width / 10;
-
- // The draw of the rect on the selected slice must happen after the other
- // drawings, otherwise it would result under another rect.
- let drawRectOnSelected = () => {};
-
- for (let i = 0; i < data.starts.length; i++) {
- const tStart = Time.fromRaw(data.starts[i]);
- let tEnd = Time.fromRaw(data.ends[i]);
- const depth = data.depths[i];
- const titleId = data.titles[i];
- const sliceId = data.sliceIds[i];
- const isInstant = data.isInstant[i];
- const isIncomplete = data.isIncomplete[i];
- const title = data.strings[titleId];
- const colorOverride = data.colors && data.strings[data.colors[i]];
- if (isIncomplete) {
- // incomplete slice
- // TODO(stevegolton): This isn't exactly equivalent, ideally we should
- // choose tEnd once we've converted to screen space coords.
- tEnd = this.getEndTimeIfInComplete(tStart);
- }
-
- if (!visibleTimeSpan.intersects(tStart, tEnd)) {
- continue;
- }
-
- const pxEnd = size.width;
- const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
- const right = Math.min(visibleTimeScale.timeToPx(tEnd), pxEnd);
-
- const rect = {
- left,
- width: Math.max(right - left, 1),
- top: TRACK_PADDING + depth * SLICE_HEIGHT,
- height: SLICE_HEIGHT,
- };
-
- const currentSelection = getLegacySelection(globals.state);
- const isSelected =
- currentSelection &&
- currentSelection.kind === 'CHROME_SLICE' &&
- currentSelection.id !== undefined &&
- currentSelection.id === sliceId;
-
- const highlighted =
- titleId === this.hoveredTitleId ||
- globals.state.highlightedSliceId === sliceId;
-
- const hasFocus = highlighted || isSelected;
- const colorScheme = getColorForSlice(title);
- const colorObj = hasFocus ? colorScheme.variant : colorScheme.base;
- const textColor = hasFocus
- ? colorScheme.textVariant
- : colorScheme.textBase;
-
- let color: string;
- if (colorOverride === undefined) {
- color = colorObj.cssString;
- } else {
- color = colorOverride;
- }
- ctx.fillStyle = color;
-
- // We draw instant events as upward facing chevrons starting at A:
- // A
- // ###
- // ##C##
- // ## ##
- // D B
- // Then B, C, D and back to A:
- if (isInstant) {
- if (isSelected) {
- drawRectOnSelected = () => {
- ctx.save();
- ctx.translate(rect.left, rect.top);
-
- // Draw a rectangle around the selected slice
- ctx.strokeStyle = colorObj.setHSL({s: 100, l: 10}).cssString;
- ctx.beginPath();
- ctx.lineWidth = 3;
- ctx.strokeRect(
- -HALF_CHEVRON_WIDTH_PX,
- 0,
- CHEVRON_WIDTH_PX,
- SLICE_HEIGHT,
- );
- ctx.closePath();
-
- // Draw inner chevron as interior
- ctx.fillStyle = color;
- this.drawChevron(ctx);
-
- ctx.restore();
- };
- } else {
- ctx.save();
- ctx.translate(rect.left, rect.top);
- this.drawChevron(ctx);
- ctx.restore();
- }
- continue;
- }
-
- if (isIncomplete && rect.width > SLICE_HEIGHT / 4) {
- drawIncompleteSlice(
- ctx,
- rect.left,
- rect.top,
- rect.width,
- SLICE_HEIGHT,
- !CROP_INCOMPLETE_SLICE_FLAG.get(),
- );
- } else if (
- data.cpuTimeRatio !== undefined &&
- data.cpuTimeRatio[i] < 1 - 1e-9
- ) {
- // We draw two rectangles, representing the ratio between wall time and
- // time spent on cpu.
- const cpuTimeRatio = data.cpuTimeRatio![i];
- const firstPartWidth = rect.width * cpuTimeRatio;
- const secondPartWidth = rect.width * (1 - cpuTimeRatio);
- ctx.fillRect(rect.left, rect.top, rect.width, SLICE_HEIGHT);
- ctx.fillStyle = '#FFFFFF50';
- ctx.fillRect(
- rect.left + firstPartWidth,
- rect.top,
- secondPartWidth,
- SLICE_HEIGHT,
- );
- } else {
- ctx.fillRect(rect.left, rect.top, rect.width, SLICE_HEIGHT);
- }
-
- // Selected case
- if (isSelected) {
- drawRectOnSelected = () => {
- ctx.strokeStyle = colorObj.setHSL({s: 100, l: 10}).cssString;
- ctx.beginPath();
- ctx.lineWidth = 3;
- ctx.strokeRect(
- rect.left,
- rect.top - 1.5,
- rect.width,
- SLICE_HEIGHT + 3,
- );
- ctx.closePath();
- };
- }
-
- // Don't render text when we have less than 5px to play with.
- if (rect.width >= 5) {
- ctx.fillStyle = textColor.cssString;
- const displayText = cropText(title, charWidth, rect.width);
- const rectXCenter = rect.left + rect.width / 2;
- ctx.textBaseline = 'middle';
- ctx.font = this.getFont();
- ctx.fillText(displayText, rectXCenter, rect.top + SLICE_HEIGHT / 2);
- }
- }
- drawRectOnSelected();
- }
-
- drawChevron(ctx: CanvasRenderingContext2D) {
- // Draw a chevron at a fixed location and size. Should be used with
- // ctx.translate and ctx.scale to alter location and size.
- ctx.beginPath();
- ctx.moveTo(0, 0);
- ctx.lineTo(HALF_CHEVRON_WIDTH_PX, SLICE_HEIGHT);
- ctx.lineTo(0, SLICE_HEIGHT - HALF_CHEVRON_WIDTH_PX);
- ctx.lineTo(-HALF_CHEVRON_WIDTH_PX, SLICE_HEIGHT);
- ctx.lineTo(0, 0);
- ctx.fill();
- }
-
- getSliceIndex({x, y}: {x: number; y: number}): number | void {
- const data = this.fetcher.data;
- if (data === undefined) return;
- const {visibleTimeScale: timeScale} = globals.timeline;
- if (y < TRACK_PADDING) return;
- const instantWidthTime = timeScale.pxDeltaToDuration(HALF_CHEVRON_WIDTH_PX);
- const t = timeScale.pxToHpTime(x);
- const depth = Math.floor((y - TRACK_PADDING) / SLICE_HEIGHT);
-
- for (let i = 0; i < data.starts.length; i++) {
- if (depth !== data.depths[i]) {
- continue;
- }
- const start = Time.fromRaw(data.starts[i]);
- const tStart = HighPrecisionTime.fromTime(start);
- if (data.isInstant[i]) {
- if (tStart.sub(t).abs().lt(instantWidthTime)) {
- return i;
- }
- } else {
- const end = Time.fromRaw(data.ends[i]);
- let tEnd = HighPrecisionTime.fromTime(end);
- if (data.isIncomplete[i]) {
- const endTime = this.getEndTimeIfInComplete(start);
- tEnd = HighPrecisionTime.fromTime(endTime);
- }
- if (tStart.lte(t) && t.lte(tEnd)) {
- return i;
- }
- }
- }
- }
-
- getEndTimeIfInComplete(start: time): time {
- const {visibleTimeScale, visibleWindowTime} = globals.timeline;
-
- let end = visibleWindowTime.end.toTime('ceil');
- if (CROP_INCOMPLETE_SLICE_FLAG.get()) {
- const widthTime = visibleTimeScale
- .pxDeltaToDuration(INCOMPLETE_SLICE_WIDTH_PX)
- .toTime();
- end = Time.add(start, widthTime);
- }
-
- return end;
- }
-
- onMouseMove({x, y}: {x: number; y: number}) {
- this.hoveredTitleId = -1;
- globals.dispatch(Actions.setHighlightedSliceId({sliceId: -1}));
- const sliceIndex = this.getSliceIndex({x, y});
- if (sliceIndex === undefined) return;
- const data = this.fetcher.data;
- if (data === undefined) return;
- this.hoveredTitleId = data.titles[sliceIndex];
- const sliceId = data.sliceIds[sliceIndex];
- globals.dispatch(Actions.setHighlightedSliceId({sliceId}));
- }
-
- onMouseOut() {
- this.hoveredTitleId = -1;
- globals.dispatch(Actions.setHighlightedSliceId({sliceId: -1}));
- }
-
- onMouseClick({x, y}: {x: number; y: number}): boolean {
- const sliceIndex = this.getSliceIndex({x, y});
- if (sliceIndex === undefined) return false;
- const data = this.fetcher.data;
- if (data === undefined) return false;
- const sliceId = data.sliceIds[sliceIndex];
- if (sliceId !== undefined && sliceId !== -1) {
- globals.setLegacySelection(
- {
- kind: 'CHROME_SLICE',
- id: sliceId,
- trackKey: this.trackKey,
- table: this.namespace,
- },
- {
- clearSearch: true,
- pendingScrollId: undefined,
- switchToCurrentSelectionTab: true,
- },
- );
- return true;
- }
- return false;
- }
-
- getHeight() {
- return SLICE_HEIGHT * (this.maxDepth + 1) + 2 * TRACK_PADDING;
- }
-
- getSliceRect(tStart: time, tEnd: time, depth: number): SliceRect | undefined {
- const {windowSpan, visibleTimeScale, visibleTimeSpan} = globals.timeline;
-
- const pxEnd = windowSpan.end;
- const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
- const right = Math.min(visibleTimeScale.timeToPx(tEnd), pxEnd);
-
- const visible = visibleTimeSpan.intersects(tStart, tEnd);
-
- return {
- left,
- width: Math.max(right - left, 1),
- top: TRACK_PADDING + depth * SLICE_HEIGHT,
- height: SLICE_HEIGHT,
- visible,
- };
- }
-}
diff --git a/ui/src/tracks/annotation/index.ts b/ui/src/tracks/annotation/index.ts
index d36e9a20d..bda66f3fd 100644
--- a/ui/src/tracks/annotation/index.ts
+++ b/ui/src/tracks/annotation/index.ts
@@ -13,8 +13,11 @@
// limitations under the License.
import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
+import {
+ ChromeSliceTrack,
+ SLICE_TRACK_KIND,
+} from '../chrome_slices/chrome_slice_track';
import {NUM, NUM_NULL, STR} from '../../trace_processor/query_result';
-import {ChromeSliceTrack, SLICE_TRACK_KIND} from '../chrome_slices/';
import {COUNTER_TRACK_KIND, TraceProcessorCounterTrack} from '../counter';
class AnnotationPlugin implements Plugin {
@@ -49,7 +52,15 @@ class AnnotationPlugin implements Plugin {
metric: true,
},
trackFactory: ({trackKey}) => {
- return new ChromeSliceTrack(engine, 0, trackKey, id, 'annotation');
+ return new ChromeSliceTrack(
+ {
+ engine: ctx.engine,
+ trackKey,
+ },
+ id,
+ 0,
+ 'annotation_slice',
+ );
},
});
}
diff --git a/ui/src/tracks/chrome_slices/chrome_slice_track.ts b/ui/src/tracks/chrome_slices/chrome_slice_track.ts
new file mode 100644
index 000000000..c9fda8bff
--- /dev/null
+++ b/ui/src/tracks/chrome_slices/chrome_slice_track.ts
@@ -0,0 +1,109 @@
+// 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 {BigintMath as BIMath} from '../../base/bigint_math';
+import {clamp} from '../../base/math_utils';
+import {OnSliceClickArgs} from '../../frontend/base_slice_track';
+import {globals} from '../../frontend/globals';
+import {
+ NAMED_ROW,
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
+import {NewTrackArgs} from '../../frontend/track';
+import {LONG_NULL} from '../../trace_processor/query_result';
+
+export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
+
+export const CHROME_SLICE_ROW = {
+ // Base columns (tsq, ts, dur, id, depth).
+ ...NAMED_ROW,
+
+ // Chrome-specific columns.
+ threadDur: LONG_NULL,
+};
+export type ChromeSliceRow = typeof CHROME_SLICE_ROW;
+
+export interface ChromeSliceTrackTypes extends NamedSliceTrackTypes {
+ row: ChromeSliceRow;
+}
+
+export class ChromeSliceTrack extends NamedSliceTrack<ChromeSliceTrackTypes> {
+ constructor(
+ args: NewTrackArgs,
+ private trackId: number,
+ maxDepth: number,
+ private tableName: string = 'slice',
+ ) {
+ super(args);
+ this.sliceLayout = {
+ ...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
+ depthGuess: maxDepth,
+ };
+ }
+
+ // This is used by the base class to call iter().
+ getRowSpec() {
+ return CHROME_SLICE_ROW;
+ }
+
+ getSqlSource(): string {
+ return `select
+ ts,
+ dur,
+ id,
+ depth,
+ ifnull(name, '') as name,
+ thread_dur as threadDur
+ from ${this.tableName}
+ where track_id = ${this.trackId}`;
+ }
+
+ // Converts a SQL result row to an "Impl" Slice.
+ rowToSlice(
+ row: ChromeSliceTrackTypes['row'],
+ ): ChromeSliceTrackTypes['slice'] {
+ const namedSlice = super.rowToSlice(row);
+
+ if (row.dur > 0n && row.threadDur !== null) {
+ const fillRatio = clamp(BIMath.ratio(row.threadDur, row.dur), 0, 1);
+ return {...namedSlice, fillRatio};
+ } else {
+ return namedSlice;
+ }
+ }
+
+ onUpdatedSlices(slices: ChromeSliceTrackTypes['slice'][]) {
+ for (const slice of slices) {
+ slice.isHighlighted = slice === this.hoveredSlice;
+ }
+ }
+
+ onSliceClick(args: OnSliceClickArgs<ChromeSliceTrackTypes['slice']>) {
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
+ id: args.slice.id,
+ trackKey: this.trackKey,
+ table: this.tableName,
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: true,
+ },
+ );
+ }
+}
diff --git a/ui/src/tracks/chrome_slices/generic_slice_track.ts b/ui/src/tracks/chrome_slices/generic_slice_track.ts
deleted file mode 100644
index 1e817a894..000000000
--- a/ui/src/tracks/chrome_slices/generic_slice_track.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 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 {
- NamedSliceTrack,
- NamedSliceTrackTypes,
-} from '../../frontend/named_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
-
-export class GenericSliceTrack extends NamedSliceTrack<NamedSliceTrackTypes> {
- constructor(args: NewTrackArgs, private sqlTrackId: number) {
- super(args);
- }
-
- getSqlSource(): string {
- return `select ts, dur, id, depth, ifnull(name, '') as name
- from slice where track_id = ${this.sqlTrackId}`;
- }
-}
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index cae255728..830b7e4e9 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -12,213 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {BigintMath as BIMath} from '../../base/bigint_math';
-import {clamp} from '../../base/math_utils';
-import {Duration, duration, time} from '../../base/time';
import {uuidv4} from '../../base/uuid';
import {ChromeSliceDetailsTab} from '../../frontend/chrome_slice_details_tab';
import {
- NAMED_ROW,
- NamedSliceTrack,
- NamedSliceTrackTypes,
-} from '../../frontend/named_slice_track';
-import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
-import {SliceData, SliceTrackLEGACY} from '../../frontend/slice_track';
-import {NewTrackArgs} from '../../frontend/track';
-import {
BottomTabToSCSAdapter,
- EngineProxy,
Plugin,
PluginContextTrace,
PluginDescriptor,
} from '../../public';
import {getTrackName} from '../../public/utils';
-import {
- LONG,
- LONG_NULL,
- NUM,
- NUM_NULL,
- STR,
- STR_NULL,
-} from '../../trace_processor/query_result';
-
-export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
-
-export class ChromeSliceTrack extends SliceTrackLEGACY {
- private maxDurNs: duration = 0n;
-
- constructor(
- protected engine: EngineProxy,
- maxDepth: number,
- trackKey: string,
- private trackId: number,
- namespace?: string,
- ) {
- super(maxDepth, trackKey, 'slice', namespace);
- }
-
- async onBoundsChange(
- start: time,
- end: time,
- resolution: duration,
- ): Promise<SliceData> {
- const tableName = this.namespaceTable('slice');
-
- if (this.maxDurNs === Duration.ZERO) {
- const query = `
- SELECT max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
- AS maxDur FROM ${tableName} WHERE track_id = ${this.trackId}`;
- const queryRes = await this.engine.query(query);
- this.maxDurNs = queryRes.firstRow({maxDur: LONG_NULL}).maxDur ?? 0n;
- }
-
- const query = `
- SELECT
- (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
- ts,
- max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
- depth,
- id as sliceId,
- ifnull(name, '[null]') as name,
- dur = 0 as isInstant,
- dur = -1 as isIncomplete,
- thread_dur as threadDur
- FROM ${tableName}
- WHERE track_id = ${this.trackId} AND
- ts >= (${start - this.maxDurNs}) AND
- ts <= ${end}
- GROUP BY depth, tsq`;
- const queryRes = await this.engine.query(query);
-
- const numRows = queryRes.numRows();
- const slices: SliceData = {
- start,
- end,
- resolution,
- length: numRows,
- strings: [],
- sliceIds: new Float64Array(numRows),
- starts: new BigInt64Array(numRows),
- ends: new BigInt64Array(numRows),
- depths: new Uint16Array(numRows),
- titles: new Uint16Array(numRows),
- isInstant: new Uint16Array(numRows),
- isIncomplete: new Uint16Array(numRows),
- cpuTimeRatio: new Float64Array(numRows),
- };
-
- const stringIndexes = new Map<string, number>();
- function internString(str: string) {
- let idx = stringIndexes.get(str);
- if (idx !== undefined) return idx;
- idx = slices.strings.length;
- slices.strings.push(str);
- stringIndexes.set(str, idx);
- return idx;
- }
-
- const it = queryRes.iter({
- tsq: LONG,
- ts: LONG,
- dur: LONG,
- depth: NUM,
- sliceId: NUM,
- name: STR,
- isInstant: NUM,
- isIncomplete: NUM,
- threadDur: LONG_NULL,
- });
- for (let row = 0; it.valid(); it.next(), row++) {
- const startQ = it.tsq;
- const start = it.ts;
- const dur = it.dur;
- const end = start + dur;
- const minEnd = startQ + resolution;
- const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
-
- slices.starts[row] = startQ;
- slices.ends[row] = endQ;
- slices.depths[row] = it.depth;
- slices.sliceIds[row] = it.sliceId;
- slices.titles[row] = internString(it.name);
- slices.isInstant[row] = it.isInstant;
- slices.isIncomplete[row] = it.isIncomplete;
-
- let cpuTimeRatio = 1;
- if (!it.isInstant && !it.isIncomplete && it.threadDur !== null) {
- // Rounding the CPU time ratio to two decimal places and ensuring
- // it is less than or equal to one, incase the thread duration exceeds
- // the total duration.
- cpuTimeRatio = Math.min(
- Math.round(BIMath.ratio(it.threadDur, it.dur) * 100) / 100,
- 1,
- );
- }
- slices.cpuTimeRatio![row] = cpuTimeRatio;
- }
- return slices;
- }
-}
-
-export const CHROME_SLICE_ROW = {
- // Base columns (tsq, ts, dur, id, depth).
- ...NAMED_ROW,
-
- // Chrome-specific columns.
- threadDur: LONG_NULL,
-};
-export type ChromeSliceRow = typeof CHROME_SLICE_ROW;
-
-export interface ChromeSliceTrackTypes extends NamedSliceTrackTypes {
- row: ChromeSliceRow;
-}
-
-export class ChromeSliceTrackV2 extends NamedSliceTrack<ChromeSliceTrackTypes> {
- constructor(args: NewTrackArgs, private trackId: number, maxDepth: number) {
- super(args);
- this.sliceLayout = {
- ...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
- depthGuess: maxDepth,
- };
- }
-
- // This is used by the base class to call iter().
- getRowSpec() {
- return CHROME_SLICE_ROW;
- }
-
- getSqlSource(): string {
- return `select
- ts,
- dur,
- id,
- depth,
- ifnull(name, '') as name,
- thread_dur as threadDur
- from slice
- where track_id = ${this.trackId}`;
- }
-
- // Converts a SQL result row to an "Impl" Slice.
- rowToSlice(
- row: ChromeSliceTrackTypes['row'],
- ): ChromeSliceTrackTypes['slice'] {
- const namedSlice = super.rowToSlice(row);
-
- if (row.dur > 0n && row.threadDur !== null) {
- const fillRatio = clamp(BIMath.ratio(row.threadDur, row.dur), 0, 1);
- return {...namedSlice, fillRatio};
- } else {
- return namedSlice;
- }
- }
-
- onUpdatedSlices(slices: ChromeSliceTrackTypes['slice'][]) {
- for (const slice of slices) {
- slice.isHighlighted = slice === this.hoveredSlice;
- }
- }
-}
+import {NUM, NUM_NULL, STR_NULL} from '../../trace_processor/query_result';
+import {ChromeSliceTrack, SLICE_TRACK_KIND} from './chrome_slice_track';
class ChromeSlicesPlugin implements Plugin {
async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
@@ -281,7 +85,7 @@ class ChromeSlicesPlugin implements Plugin {
engine: ctx.engine,
trackKey,
};
- return new ChromeSliceTrackV2(newTrackArgs, trackId, maxDepth);
+ return new ChromeSliceTrack(newTrackArgs, trackId, maxDepth);
},
});
}
diff --git a/ui/src/tracks/visualised_args/index.ts b/ui/src/tracks/visualised_args/index.ts
index 9e34de3f1..459a7ad4a 100644
--- a/ui/src/tracks/visualised_args/index.ts
+++ b/ui/src/tracks/visualised_args/index.ts
@@ -12,98 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import m from 'mithril';
-import {v4 as uuidv4} from 'uuid';
-
-import {Actions} from '../../common/actions';
-import {globals} from '../../frontend/globals';
-import {
- EngineProxy,
- Plugin,
- PluginContextTrace,
- PluginDescriptor,
- TrackContext,
-} from '../../public';
-import {ChromeSliceTrack} from '../chrome_slices';
+import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
import {
VISUALISED_ARGS_SLICE_TRACK_URI,
VisualisedArgsState,
} from '../../frontend/visualized_args_tracks';
-import {Button} from '../../widgets/button';
-import {Icons} from '../../base/semantic_icons';
-
-export class VisualisedArgsTrack extends ChromeSliceTrack {
- private helperViewName: string;
-
- constructor(
- engine: EngineProxy,
- maxDepth: number,
- trackKey: string,
- trackId: number,
- private argName: string,
- ) {
- const uuid = uuidv4();
- const namespace = `__arg_visualisation_helper_${argName}_${uuid}`;
- const escapedNamespace = namespace.replace(/[^a-zA-Z]/g, '_');
- super(engine, maxDepth, trackKey, trackId, escapedNamespace);
- this.helperViewName = `${escapedNamespace}_slice`;
- }
-
- async onCreate(_ctx: TrackContext): Promise<void> {
- // Create the helper view - just one which is relevant to this slice
- await this.engine.query(`
- create view ${this.helperViewName} as
- with slice_with_arg as (
- select
- slice.id,
- slice.track_id,
- slice.ts,
- slice.dur,
- slice.thread_dur,
- NULL as cat,
- args.display_value as name
- from slice
- join args using (arg_set_id)
- where args.key='${this.argName}'
- )
- select
- *,
- (select count()
- from ancestor_slice(s1.id) s2
- join slice_with_arg s3 on s2.id=s3.id
- ) as depth
- from slice_with_arg s1
- order by id;
- `);
- }
-
- async onDestroy(): Promise<void> {
- if (this.engine.isAlive) {
- await this.engine.query(`drop view ${this.helperViewName}`);
- }
- }
-
- getFont() {
- return 'italic 11px Roboto';
- }
-
- getTrackShellButtons(): m.Children {
- return m(Button, {
- onclick: () => {
- // This behavior differs to the original behavior a little.
- // Originally, hitting the close button on a single track removed ALL
- // tracks with this argName, whereas this one only closes the single
- // track.
- // This will be easily fixable once we transition to using dynamic
- // tracks instead of this "initial state" approach to add these tracks.
- globals.dispatch(Actions.removeTracks({trackKeys: [this.trackKey]}));
- },
- icon: Icons.Close,
- title: 'Close',
- compact: true,
- });
- }
-}
+import {VisualisedArgsTrack} from './visualized_args_track';
class VisualisedArgsPlugin implements Plugin {
async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
@@ -117,10 +31,12 @@ class VisualisedArgsPlugin implements Plugin {
// worse than the situation we had before with track config.
const params = trackCtx.params as VisualisedArgsState;
return new VisualisedArgsTrack(
- ctx.engine,
- params.maxDepth,
- trackCtx.trackKey,
+ {
+ engine: ctx.engine,
+ trackKey: trackCtx.trackKey,
+ },
params.trackId,
+ params.maxDepth,
params.argName,
);
},
diff --git a/ui/src/tracks/visualised_args/visualized_args_track.ts b/ui/src/tracks/visualised_args/visualized_args_track.ts
new file mode 100644
index 000000000..8de85d34f
--- /dev/null
+++ b/ui/src/tracks/visualised_args/visualized_args_track.ts
@@ -0,0 +1,93 @@
+// 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 m from 'mithril';
+
+import {Actions} from '../../common/actions';
+import {globals} from '../../frontend/globals';
+import {Button} from '../../widgets/button';
+import {Icons} from '../../base/semantic_icons';
+import {ChromeSliceTrack} from '../chrome_slices/chrome_slice_track';
+import {uuidv4Sql} from '../../base/uuid';
+import {NewTrackArgs} from '../../frontend/track';
+import {Disposable, DisposableCallback} from '../../base/disposable';
+
+// Similar to a SliceTrack, but creates a view
+export class VisualisedArgsTrack extends ChromeSliceTrack {
+ private viewName: string;
+
+ constructor(
+ args: NewTrackArgs,
+ trackId: number,
+ maxDepth: number,
+ private argName: string,
+ ) {
+ const uuid = uuidv4Sql();
+ const escapedArgName = argName.replace(/[^a-zA-Z]/g, '_');
+ const viewName = `__arg_visualisation_helper_${escapedArgName}_${uuid}_slice`;
+ super(args, trackId, maxDepth, viewName);
+ this.viewName = viewName;
+ }
+
+ async onInit(): Promise<Disposable> {
+ // Create the helper view - just one which is relevant to this slice
+ await this.engine.query(`
+ create view ${this.viewName} as
+ with slice_with_arg as (
+ select
+ slice.id,
+ slice.track_id,
+ slice.ts,
+ slice.dur,
+ slice.thread_dur,
+ NULL as cat,
+ args.display_value as name
+ from slice
+ join args using (arg_set_id)
+ where args.key='${this.argName}'
+ )
+ select
+ *,
+ (select count()
+ from ancestor_slice(s1.id) s2
+ join slice_with_arg s3 on s2.id=s3.id
+ ) as depth
+ from slice_with_arg s1
+ order by id;
+ `);
+
+ return new DisposableCallback(() => {
+ if (this.engine.isAlive) {
+ this.engine.query(`drop view ${this.viewName}`);
+ }
+ });
+ }
+
+ getTrackShellButtons(): m.Children {
+ return m(Button, {
+ onclick: () => {
+ // This behavior differs to the original behavior a little.
+ // Originally, hitting the close button on a single track removed ALL
+ // tracks with this argName, whereas this one only closes the single
+ // track.
+ // This will be easily fixable once we transition to using dynamic
+ // tracks instead of this "initial state" approach to add these tracks.
+ globals.dispatch(Actions.removeTracks({trackKeys: [this.trackKey]}));
+ },
+ icon: Icons.Close,
+ title: 'Close',
+ compact: true,
+ });
+ }
+}