• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
17import {LegacySelection} from '../common/state';
18import {BottomTab} from '../frontend/bottom_tab';
19
20import {LegacyDetailsPanel, Tab} from '.';
21
22export function getTrackName(
23  args: Partial<{
24    name: string | null;
25    utid: number | null;
26    processName: string | null;
27    pid: number | null;
28    threadName: string | null;
29    tid: number | null;
30    upid: number | null;
31    userName: string | null;
32    uid: number | null;
33    kind: string;
34    threadTrack: boolean;
35    uidTrack: boolean;
36  }>,
37) {
38  const {
39    name,
40    upid,
41    utid,
42    processName,
43    threadName,
44    pid,
45    tid,
46    userName,
47    uid,
48    kind,
49    threadTrack,
50    uidTrack,
51  } = args;
52
53  const hasName = name !== undefined && name !== null && name !== '[NULL]';
54  const hasUpid = upid !== undefined && upid !== null;
55  const hasUtid = utid !== undefined && utid !== null;
56  const hasProcessName = processName !== undefined && processName !== null;
57  const hasThreadName = threadName !== undefined && threadName !== null;
58  const hasUserName = userName !== undefined && userName !== null;
59  const hasTid = tid !== undefined && tid !== null;
60  const hasPid = pid !== undefined && pid !== null;
61  const hasUid = uid !== undefined && uid !== null;
62  const hasKind = kind !== undefined;
63  const isThreadTrack = threadTrack !== undefined && threadTrack;
64  const isUidTrack = uidTrack !== undefined && uidTrack;
65
66  // If we don't have any useful information (better than
67  // upid/utid) we show the track kind to help with tracking
68  // down where this is coming from.
69  const kindSuffix = hasKind ? ` (${kind})` : '';
70
71  if (isThreadTrack && hasName && hasTid) {
72    return `${name} (${tid})`;
73  } else if (isUidTrack && hasName && hasUserName) {
74    return `${name} (${userName})`;
75  } else if (isUidTrack && hasName && hasUid) {
76    return `${name} ${uid}`;
77  } else if (hasName) {
78    return `${name}`;
79  } else if (hasUpid && hasPid && hasProcessName) {
80    return `${processName} ${pid}`;
81  } else if (hasUpid && hasPid) {
82    return `Process ${pid}`;
83  } else if (hasThreadName && hasTid) {
84    return `${threadName} ${tid}`;
85  } else if (hasTid) {
86    return `Thread ${tid}`;
87  } else if (hasUpid) {
88    return `upid: ${upid}${kindSuffix}`;
89  } else if (hasUtid) {
90    return `utid: ${utid}${kindSuffix}`;
91  } else if (hasUid) {
92    return `uid: ${uid}${kindSuffix}`;
93  } else if (hasKind) {
94    return `Unnamed ${kind}`;
95  }
96  return 'Unknown';
97}
98
99export interface BottomTabAdapterAttrs {
100  tabFactory: (sel: LegacySelection) => BottomTab | undefined;
101}
102
103/**
104 * This adapter wraps a BottomTab, converting it into a the new "current
105 * selection" API.
106 * This adapter is required because most bottom tab implementations expect to
107 * be created when the selection changes, however current selection sections
108 * stick around in memory forever and produce a section only when they detect a
109 * relevant selection.
110 * This adapter, given a bottom tab factory function, will simply call the
111 * factory function whenever the selection changes. It's up to the implementer
112 * to work out whether the selection is relevant and to construct a bottom tab.
113 *
114 * @example
115 * new BottomTabAdapter({
116      tabFactory: (sel) => {
117        if (sel.kind !== 'SLICE') {
118          return undefined;
119        }
120        return new ChromeSliceDetailsTab({
121          config: {
122            table: sel.table ?? 'slice',
123            id: sel.id,
124          },
125          engine: ctx.engine,
126          uuid: uuidv4(),
127        });
128      },
129    })
130 */
131export class BottomTabToSCSAdapter implements LegacyDetailsPanel {
132  private oldSelection?: LegacySelection;
133  private bottomTab?: BottomTab;
134  private attrs: BottomTabAdapterAttrs;
135
136  constructor(attrs: BottomTabAdapterAttrs) {
137    this.attrs = attrs;
138  }
139
140  render(selection: LegacySelection): m.Children {
141    // Detect selection changes, assuming selection is immutable
142    if (selection !== this.oldSelection) {
143      this.oldSelection = selection;
144      this.bottomTab = this.attrs.tabFactory(selection);
145    }
146
147    return this.bottomTab?.renderPanel();
148  }
149
150  // Note: Must be called after render()
151  isLoading(): boolean {
152    return this.bottomTab?.isLoading() ?? false;
153  }
154}
155
156/**
157 * This adapter wraps a BottomTab, converting it to work with the Tab API.
158 */
159export class BottomTabToTabAdapter implements Tab {
160  constructor(private bottomTab: BottomTab) {}
161
162  getTitle(): string {
163    return this.bottomTab.getTitle();
164  }
165
166  render(): m.Children {
167    return this.bottomTab.viewTab();
168  }
169}
170