• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2024 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';
16import {duration, time, TimeSpan} from '../base/time';
17import {Dataset, DatasetSchema} from '../trace_processor/dataset';
18import {Engine} from '../trace_processor/engine';
19import {ColumnDef, Sorting, ThreadStateExtra} from './aggregation';
20import {Track} from './track';
21import {arrayEquals} from '../base/array_utils';
22
23export interface ContentWithLoadingFlag {
24  readonly isLoading: boolean;
25  readonly content: m.Children;
26}
27
28export interface AreaSelectionTab {
29  // Unique id for this tab.
30  readonly id: string;
31
32  // A name for this tab.
33  readonly name: string;
34
35  // Defines the sort order of this tab - higher values appear first.
36  readonly priority?: number;
37
38  /**
39   * Called every Mithril render cycle to render the content of the tab. The
40   * returned content will be displayed inside the current selection tab.
41   *
42   * If undefined is returned then the tab handle will be hidden, which gives
43   * the tab the option to dynamically remove itself from the list of tabs if it
44   * has nothing relevant to show.
45   *
46   * The |isLoading| flag is used to avoid flickering. If set to true, we keep
47   * hold of the the previous vnodes, rendering them instead, for up to 50ms
48   * before switching to the new content. This avoids very fast load times
49   * from causing flickering loading screens, which can be somewhat jarring.
50   */
51  render(selection: AreaSelection): ContentWithLoadingFlag | undefined;
52}
53
54/**
55 * Compare two area selections for equality. Returns true if the selections are
56 * equivalent, false otherwise.
57 */
58export function areaSelectionsEqual(a: AreaSelection, b: AreaSelection) {
59  if (a.start !== b.start) return false;
60  if (a.end !== b.end) return false;
61  if (!arrayEquals(a.trackUris, b.trackUris)) {
62    return false;
63  }
64  return true;
65}
66
67export interface SelectionManager {
68  readonly selection: Selection;
69
70  /**
71   * Provides a list of registered area selection tabs.
72   */
73  readonly areaSelectionTabs: ReadonlyArray<AreaSelectionTab>;
74
75  findTimeRangeOfSelection(): TimeSpan | undefined;
76  clear(): void;
77
78  /**
79   * Select a track event.
80   *
81   * @param trackUri - The URI of the track to select.
82   * @param eventId - The value of the events ID column.
83   * @param opts - Additional options.
84   */
85  selectTrackEvent(
86    trackUri: string,
87    eventId: number,
88    opts?: SelectionOpts,
89  ): void;
90
91  /**
92   * Select a track.
93   *
94   * @param trackUri - The URI for the track to select.
95   * @param opts - Additional options.
96   */
97  selectTrack(trackUri: string, opts?: SelectionOpts): void;
98
99  /**
100   * Select a track event via a sql table name + id.
101   *
102   * @param sqlTableName - The name of the SQL table to resolve.
103   * @param id - The ID of the event in that table.
104   * @param opts - Additional options.
105   */
106  selectSqlEvent(sqlTableName: string, id: number, opts?: SelectionOpts): void;
107
108  /**
109   * Create an area selection for the purposes of aggregation.
110   *
111   * @param args - The area to select.
112   * @param opts - Additional options.
113   */
114  selectArea(args: Area, opts?: SelectionOpts): void;
115
116  scrollToCurrentSelection(): void;
117
118  /**
119   * Register a new tab under the area selection details panel.
120   */
121  registerAreaSelectionTab(tab: AreaSelectionTab): void;
122}
123
124/**
125 * Aggregator tabs are displayed in descending order of specificity, determined
126 * by the following precedence hierarchy:
127 * 1. Aggregators explicitly defining a `trackKind` string take priority over
128 *    those that do not.
129 * 2. Otherwise, aggregators with schemas containing a greater number of keys
130 *    (higher specificity) are prioritized over those with fewer keys.
131 * 3. In cases of identical specificity, tabs are ranked based on their
132 *    registration order.
133 */
134export interface AreaSelectionAggregator {
135  readonly id: string;
136
137  /**
138   * If defined, the dataset passed to `createAggregateView` will only contain
139   * tracks with a matching `kind` tag.
140   */
141  readonly trackKind?: string;
142
143  /**
144   * If defined, the dataset passed to `createAggregateView` will only contain
145   * tracks that export datasets that implement this schema.
146   */
147  readonly schema?: DatasetSchema;
148
149  /**
150   * Creates a view for the aggregated data corresponding to the selected area.
151   *
152   * The dataset provided will be filtered based on the `trackKind` and `schema`
153   * if these properties are defined.
154   *
155   * @param engine - The query engine used to execute queries.
156   * @param area - The currently selected area to aggregate.
157   * @param dataset - The dataset representing a union of the data in the
158   * selected tracks.
159   */
160  createAggregateView(
161    engine: Engine,
162    area: AreaSelection,
163    dataset?: Dataset,
164  ): Promise<boolean>;
165  getExtra(
166    engine: Engine,
167    area: AreaSelection,
168    dataset?: Dataset,
169  ): Promise<ThreadStateExtra | void>;
170  getTabName(): string;
171  getDefaultSorting(): Sorting;
172  getColumnDefinitions(): ColumnDef[];
173}
174
175export type Selection =
176  | TrackEventSelection
177  | TrackSelection
178  | AreaSelection
179  | NoteSelection
180  | EmptySelection;
181
182/** Defines how changes to selection affect the rest of the UI state */
183export interface SelectionOpts {
184  clearSearch?: boolean; // Default: true.
185  switchToCurrentSelectionTab?: boolean; // Default: true.
186  scrollToSelection?: boolean; // Default: false.
187}
188
189export interface TrackEventSelection extends TrackEventDetails {
190  readonly kind: 'track_event';
191  readonly trackUri: string;
192  readonly eventId: number;
193}
194
195export interface TrackSelection {
196  readonly kind: 'track';
197  readonly trackUri: string;
198}
199
200export interface TrackEventDetails {
201  // ts and dur are required by the core, and must be provided.
202  readonly ts: time;
203
204  // Note: dur can be 0 for instant events or -1 for DNF slices. Will be
205  // undefined if this selection has no duration, i.e. profile / counter
206  // samples.
207  readonly dur?: duration;
208
209  // Optional additional information.
210  // TODO(stevegolton): Find an elegant way of moving this information out of
211  // the core.
212  readonly wakeupTs?: time;
213  readonly wakerCpu?: number;
214  readonly utid?: number;
215}
216
217export interface Area {
218  readonly start: time;
219  readonly end: time;
220  // TODO(primiano): this should be ReadonlyArray<> after the pivot table state
221  // doesn't use State/Immer anymore.
222  readonly trackUris: string[];
223}
224
225export interface AreaSelection extends Area {
226  readonly kind: 'area';
227
228  // This array contains the resolved Tracks from Area.trackUris. The resolution
229  // is done by SelectionManager whenever a kind='area' selection is performed.
230  readonly tracks: ReadonlyArray<Track>;
231}
232
233export interface NoteSelection {
234  readonly kind: 'note';
235  readonly id: string;
236}
237
238export interface EmptySelection {
239  readonly kind: 'empty';
240}
241
242export interface SqlSelectionResolver {
243  readonly sqlTableName: string;
244  readonly callback: (
245    id: number,
246    sqlTable: string,
247  ) => Promise<{trackUri: string; eventId: number} | undefined>;
248}
249