• 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 {duration, time} from '../base/time';
16import {Store} from '../base/store';
17import {assertUnreachable} from '../base/logging';
18import {GenericSliceDetailsTabConfigBase} from './generic_slice_details_types';
19
20export enum ProfileType {
21  HEAP_PROFILE = 'heap_profile',
22  MIXED_HEAP_PROFILE = 'heap_profile:com.android.art,libc.malloc',
23  NATIVE_HEAP_PROFILE = 'heap_profile:libc.malloc',
24  JAVA_HEAP_SAMPLES = 'heap_profile:com.android.art',
25  JAVA_HEAP_GRAPH = 'graph',
26  PERF_SAMPLE = 'perf',
27}
28
29// LEGACY Selection types:
30export interface SliceSelection {
31  kind: 'SCHED_SLICE';
32  id: number;
33}
34
35export interface HeapProfileSelection {
36  kind: 'HEAP_PROFILE';
37  id: number;
38  upid: number;
39  ts: time;
40  type: ProfileType;
41}
42
43export interface PerfSamplesSelection {
44  kind: 'PERF_SAMPLES';
45  id: number;
46  upid: number;
47  leftTs: time;
48  rightTs: time;
49  type: ProfileType;
50}
51
52export interface CpuProfileSampleSelection {
53  kind: 'CPU_PROFILE_SAMPLE';
54  id: number;
55  utid: number;
56  ts: time;
57}
58
59export interface ThreadSliceSelection {
60  kind: 'SLICE';
61  id: number;
62  table?: string;
63}
64
65export interface ThreadStateSelection {
66  kind: 'THREAD_STATE';
67  id: number;
68}
69
70export interface LogSelection {
71  kind: 'LOG';
72  id: number;
73  trackKey: string;
74}
75
76export interface GenericSliceSelection {
77  kind: 'GENERIC_SLICE';
78  id: number;
79  sqlTableName: string;
80  start: time;
81  duration: duration;
82  // NOTE: this config can be expanded for multiple details panel types.
83  detailsPanelConfig: {kind: string; config: GenericSliceDetailsTabConfigBase};
84}
85
86export type LegacySelection = (
87  | SliceSelection
88  | HeapProfileSelection
89  | CpuProfileSampleSelection
90  | ThreadSliceSelection
91  | ThreadStateSelection
92  | PerfSamplesSelection
93  | LogSelection
94  | GenericSliceSelection
95) & {trackKey?: string};
96export type SelectionKind = LegacySelection['kind']; // 'THREAD_STATE' | 'SLICE' ...
97
98// New Selection types:
99export interface LegacySelectionWrapper {
100  kind: 'legacy';
101  legacySelection: LegacySelection;
102}
103
104export interface SingleSelection {
105  kind: 'single';
106  trackKey: string;
107  eventId: number;
108}
109
110export interface AreaSelection {
111  kind: 'area';
112  tracks: string[];
113  start: time;
114  end: time;
115}
116
117export interface NoteSelection {
118  kind: 'note';
119  id: string;
120}
121
122export interface UnionSelection {
123  kind: 'union';
124  selections: Selection[];
125}
126
127export interface EmptySelection {
128  kind: 'empty';
129}
130
131export type Selection =
132  | SingleSelection
133  | AreaSelection
134  | NoteSelection
135  | UnionSelection
136  | EmptySelection
137  | LegacySelectionWrapper;
138
139export function selectionToLegacySelection(
140  selection: Selection,
141): LegacySelection | null {
142  switch (selection.kind) {
143    case 'area':
144    case 'single':
145    case 'empty':
146    case 'note':
147      return null;
148    case 'union':
149      for (const child of selection.selections) {
150        const result = selectionToLegacySelection(child);
151        if (result !== null) {
152          return result;
153        }
154      }
155      return null;
156    case 'legacy':
157      return selection.legacySelection;
158    default:
159      assertUnreachable(selection);
160      return null;
161  }
162}
163
164interface SelectionState {
165  selection: Selection;
166}
167
168export class SelectionManager {
169  private store: Store<SelectionState>;
170
171  constructor(store: Store<SelectionState>) {
172    this.store = store;
173  }
174
175  clear(): void {
176    this.store.edit((draft) => {
177      draft.selection = {
178        kind: 'empty',
179      };
180    });
181  }
182
183  private addSelection(selection: Selection): void {
184    this.store.edit((draft) => {
185      switch (draft.selection.kind) {
186        case 'empty':
187          draft.selection = selection;
188          break;
189        case 'union':
190          draft.selection.selections.push(selection);
191          break;
192        case 'single':
193        case 'legacy':
194        case 'area':
195        case 'note':
196          draft.selection = {
197            kind: 'union',
198            selections: [draft.selection, selection],
199          };
200          break;
201        default:
202          assertUnreachable(draft.selection);
203          break;
204      }
205    });
206  }
207
208  // There is no matching addLegacy as we did not support multi-single
209  // selection with the legacy selection system.
210  setLegacy(legacySelection: LegacySelection): void {
211    this.clear();
212    this.addSelection({
213      kind: 'legacy',
214      legacySelection,
215    });
216  }
217
218  setEvent(
219    trackKey: string,
220    eventId: number,
221    legacySelection?: LegacySelection,
222  ) {
223    this.clear();
224    this.addEvent(trackKey, eventId, legacySelection);
225  }
226
227  addEvent(
228    trackKey: string,
229    eventId: number,
230    legacySelection?: LegacySelection,
231  ) {
232    this.addSelection({
233      kind: 'single',
234      trackKey,
235      eventId,
236    });
237    if (legacySelection) {
238      this.addSelection({
239        kind: 'legacy',
240        legacySelection,
241      });
242    }
243  }
244}
245