blob: f5d3d7a3c945029334afea4657a0fb6d55cabe5c [file] [log] [blame]
// 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 m from 'mithril';
import {LegacySelection} from '../common/state';
import {BottomTab} from '../frontend/bottom_tab';
import {DetailsPanel, Tab} from '.';
export function getTrackName(
args: Partial<{
name: string | null;
utid: number | null;
processName: string | null;
pid: number | null;
threadName: string | null;
tid: number | null;
upid: number | null;
userName: string | null;
uid: number | null;
kind: string;
threadTrack: boolean;
uidTrack: boolean;
}>,
) {
const {
name,
upid,
utid,
processName,
threadName,
pid,
tid,
userName,
uid,
kind,
threadTrack,
uidTrack,
} = args;
const hasName = name !== undefined && name !== null && name !== '[NULL]';
const hasUpid = upid !== undefined && upid !== null;
const hasUtid = utid !== undefined && utid !== null;
const hasProcessName = processName !== undefined && processName !== null;
const hasThreadName = threadName !== undefined && threadName !== null;
const hasUserName = userName !== undefined && userName !== null;
const hasTid = tid !== undefined && tid !== null;
const hasPid = pid !== undefined && pid !== null;
const hasUid = uid !== undefined && uid !== null;
const hasKind = kind !== undefined;
const isThreadTrack = threadTrack !== undefined && threadTrack;
const isUidTrack = uidTrack !== undefined && uidTrack;
// If we don't have any useful information (better than
// upid/utid) we show the track kind to help with tracking
// down where this is coming from.
const kindSuffix = hasKind ? ` (${kind})` : '';
if (isThreadTrack && hasName && hasTid) {
return `${name} (${tid})`;
} else if (isUidTrack && hasName && hasUserName) {
return `${name} (${userName})`;
} else if (isUidTrack && hasName && hasUid) {
return `${name} ${uid}`;
} else if (hasName) {
return `${name}`;
} else if (hasUpid && hasPid && hasProcessName) {
return `${processName} ${pid}`;
} else if (hasUpid && hasPid) {
return `Process ${pid}`;
} else if (hasThreadName && hasTid) {
return `${threadName} ${tid}`;
} else if (hasTid) {
return `Thread ${tid}`;
} else if (hasUpid) {
return `upid: ${upid}${kindSuffix}`;
} else if (hasUtid) {
return `utid: ${utid}${kindSuffix}`;
} else if (hasUid) {
return `uid: ${uid}${kindSuffix}`;
} else if (hasKind) {
return `Unnamed ${kind}`;
}
return 'Unknown';
}
export interface BottomTabAdapterAttrs {
tabFactory: (sel: LegacySelection) => BottomTab | undefined;
}
/**
* This adapter wraps a BottomTab, converting it into a the new "current
* selection" API.
* This adapter is required because most bottom tab implementations expect to
* be created when the selection changes, however current selection sections
* stick around in memory forever and produce a section only when they detect a
* relevant selection.
* This adapter, given a bottom tab factory function, will simply call the
* factory function whenever the selection changes. It's up to the implementer
* to work out whether the selection is relevant and to construct a bottom tab.
*
* @example
* new BottomTabAdapter({
tabFactory: (sel) => {
if (sel.kind !== 'CHROME_SLICE') {
return undefined;
}
return new ChromeSliceDetailsTab({
config: {
table: sel.table ?? 'slice',
id: sel.id,
},
engine: ctx.engine,
uuid: uuidv4(),
});
},
})
*/
export class BottomTabToSCSAdapter implements DetailsPanel {
private oldSelection?: LegacySelection;
private bottomTab?: BottomTab;
private attrs: BottomTabAdapterAttrs;
constructor(attrs: BottomTabAdapterAttrs) {
this.attrs = attrs;
}
render(selection: LegacySelection): m.Children {
// Detect selection changes, assuming selection is immutable
if (selection !== this.oldSelection) {
this.oldSelection = selection;
this.bottomTab = this.attrs.tabFactory(selection);
}
return this.bottomTab?.renderPanel();
}
// Note: Must be called after render()
isLoading(): boolean {
return this.bottomTab?.isLoading() ?? false;
}
}
/**
* This adapter wraps a BottomTab, converting it to work with the Tab API.
*/
export class BottomTabToTabAdapter implements Tab {
constructor(private bottomTab: BottomTab) {}
getTitle(): string {
return this.bottomTab.getTitle();
}
render(): m.Children {
return this.bottomTab.viewTab();
}
}