// Copyright (C) 2022 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 {Hotkey} from '../base/hotkeys'; import {TimeSpan, duration, time} from '../base/time'; import {Migrate, Store} from '../base/store'; import {ColorScheme} from '../core/colorizer'; import {PrimaryTrackSortKey} from '../common/state'; import {Engine} from '../trace_processor/engine'; import {PromptOption} from '../frontend/omnibox_manager'; import {LegacyDetailsPanel, TrackDescriptor} from './tracks'; import {TraceContext} from '../frontend/trace_context'; export {Engine} from '../trace_processor/engine'; export { LONG, LONG_NULL, NUM, NUM_NULL, STR, STR_NULL, } from '../trace_processor/query_result'; export {BottomTabToSCSAdapter} from './utils'; export {createStore, Migrate, Store} from '../base/store'; export {PromptOption} from '../frontend/omnibox_manager'; export {PrimaryTrackSortKey} from '../common/state'; export {addDebugSliceTrack} from '../frontend/debug_tracks/debug_tracks'; export * from '../core/track_kinds'; export { TrackDescriptor, Track, TrackContext, TrackTags, SliceRect, DetailsPanel, LegacyDetailsPanel, TrackSelectionDetailsPanel, } from './tracks'; export interface Slice { // These properties are updated only once per query result when the Slice // object is created and don't change afterwards. readonly id: number; readonly startNs: time; readonly endNs: time; readonly durNs: duration; readonly ts: time; readonly dur: duration; readonly depth: number; readonly flags: number; // Each slice can represent some extra numerical information by rendering a // portion of the slice with a lighter tint. // |fillRatio\ describes the ratio of the normal area to the tinted area // width of the slice, normalized between 0.0 -> 1.0. // 0.0 means the whole slice is tinted. // 1.0 means none of the slice is tinted. // E.g. If |fillRatio| = 0.65 the slice will be rendered like this: // [############|*******] // ^------------^-------^ // Normal Light readonly fillRatio: number; // These can be changed by the Impl. title: string; subTitle: string; colorScheme: ColorScheme; isHighlighted: boolean; } export interface Command { // A unique id for this command. id: string; // A human-friendly name for this command. name: string; // Callback is called when the command is invoked. // eslint-disable-next-line @typescript-eslint/no-explicit-any callback: (...args: any[]) => any; // Default hotkey for this command. // Note: this is just the default and may be changed by the user. // Examples: // - 'P' // - 'Shift+P' // - '!Mod+Shift+P' // See hotkeys.ts for guidance on hotkey syntax. defaultHotkey?: Hotkey; } export interface MetricVisualisation { // The name of the metric e.g. 'android_camera' metric: string; // A vega or vega-lite visualisation spec. // The data from the metric under path will be exposed as a // datasource named "metric" in Vega(-Lite) spec: string; // A path index into the metric. // For example if the metric returns the folowing protobuf: // { // foo { // bar { // baz: { name: "a" } // baz: { name: "b" } // baz: { name: "c" } // } // } // } // That becomes the following json: // { "foo": { "bar": { "baz": [ // {"name": "a"}, // {"name": "b"}, // {"name": "c"}, // ]}}} // And given path = ["foo", "bar", "baz"] // We extract: // [ {"name": "a"}, {"name": "b"}, {"name": "c"} ] // And pass that to the vega(-lite) visualisation. path: string[]; } // This interface defines a context for a plugin, which is an object passed to // most hooks within the plugin. It should be used to interact with Perfetto. export interface PluginContext { // The unique ID for this plugin. readonly pluginId: string; // Register command against this plugin context. registerCommand(command: Command): void; // Run a command, optionally passing some args. // eslint-disable-next-line @typescript-eslint/no-explicit-any runCommand(id: string, ...args: any[]): any; // Control of the sidebar. sidebar: { // Show the sidebar. show(): void; // Hide the sidebar. hide(): void; // Returns true if the sidebar is visible. isVisible(): boolean; }; } export interface SliceTrackColNames { ts: string; name: string; dur: string; } export interface DebugSliceTrackArgs { // Title of the track. If omitted a placeholder name will be chosen instead. trackName?: string; // Mapping definitions of the 'ts', 'dur', and 'name' columns. // By default, columns called ts, dur and name will be used. // If dur is assigned the value '0', all slices shall be instant events. columnMapping?: Partial<SliceTrackColNames>; // Any extra columns to be used as args. args?: string[]; // Optional renaming of columns. columns?: string[]; } export interface CounterTrackColNames { ts: string; value: string; } export interface DebugCounterTrackArgs { // Title of the track. If omitted a placeholder name will be chosen instead. trackName?: string; // Mapping definitions of the ts and value columns. columnMapping?: Partial<CounterTrackColNames>; } export interface Tab { render(): m.Children; getTitle(): string; } export interface TabDescriptor { uri: string; // TODO(stevegolton): Maybe optional for ephemeral tabs. content: Tab; isEphemeral?: boolean; // Defaults false onHide?(): void; onShow?(): void; } // Similar to PluginContext but with additional methods to operate on the // currently loaded trace. Passed to trace-relevant hooks on a plugin instead of // PluginContext. export interface PluginContextTrace extends PluginContext { readonly engine: Engine; // Control over the main timeline. timeline: { // Add a new track to the scrolling track section, returning the newly // created track key. addTrack(uri: string, displayName: string, params?: unknown): string; // Remove a single track from the timeline. removeTrack(key: string): void; // Pin a single track. pinTrack(key: string): void; // Unpin a single track. unpinTrack(key: string): void; // Pin all tracks that match a predicate. pinTracksByPredicate(predicate: TrackPredicate): void; // Unpin all tracks that match a predicate. unpinTracksByPredicate(predicate: TrackPredicate): void; // Remove all tracks that match a predicate. removeTracksByPredicate(predicate: TrackPredicate): void; // Expand all groups that match a predicate. expandGroupsByPredicate(predicate: GroupPredicate): void; // Collapse all groups that match a predicate. collapseGroupsByPredicate(predicate: GroupPredicate): void; // Retrieve a list of tracks on the timeline. tracks: TrackRef[]; // Bring a timestamp into view. panToTimestamp(ts: time): void; // Move the viewport setViewportTime(start: time, end: time): void; // A span representing the current viewport location readonly viewport: TimeSpan; }; // Control over the bottom details pane. tabs: { // Creates a new tab running the provided query. openQuery(query: string, title: string): void; // Add a tab to the tab bar (if not already) and focus it. showTab(uri: string): void; // Remove a tab from the tab bar. hideTab(uri: string): void; }; // Register a new track against a unique key known as a URI. // Once a track is registered it can be referenced multiple times on the // timeline with different params to allow customising each instance. registerTrack(trackDesc: TrackDescriptor): void; // Add a new entry to the pool of default tracks. Default tracks are a list // of track references that describe the list of tracks that should be added // to the main timeline on startup. // Default tracks are only used when a trace is first loaded, not when // loading from a permalink, where the existing list of tracks from the // shared state is used instead. addDefaultTrack(track: TrackRef): void; // Simultaneously register a track and add it as a default track in one go. // This is simply a helper which calls registerTrack() and addDefaultTrack() // with the same URI. registerStaticTrack(track: TrackDescriptor & TrackRef): void; // Register a new tab for this plugin. Will be unregistered when the plugin // is deactivated or when the trace is unloaded. registerTab(tab: TabDescriptor): void; // Suggest that a tab should be shown immediately. addDefaultTab(uri: string): void; // Register a hook into the current selection tab rendering logic that allows // customization of the current selection tab content. registerDetailsPanel(sel: LegacyDetailsPanel): void; // Create a store mounted over the top of this plugin's persistent state. mountStore<T>(migrate: Migrate<T>): Store<T>; readonly trace: TraceContext; // When the trace is opened via postMessage deep-linking, returns the sub-set // of postMessageData.pluginArgs[pluginId] for the current plugin. If not // present returns undefined. readonly openerPluginArgs?: {[key: string]: unknown}; prompt(text: string, options?: PromptOption[]): Promise<string>; } export interface Plugin { // Lifecycle methods. onActivate?(ctx: PluginContext): void; onTraceLoad?(ctx: PluginContextTrace): Promise<void>; onTraceUnload?(ctx: PluginContextTrace): Promise<void>; onDeactivate?(ctx: PluginContext): void; // Extension points. metricVisualisations?(ctx: PluginContext): MetricVisualisation[]; } // This interface defines what a plugin factory should look like. // This can be defined in the plugin class definition by defining a constructor // and the relevant static methods: // E.g. // class MyPlugin implements TracePlugin<MyState> { // migrate(initialState: unknown): MyState {...} // constructor(store: Store<MyState>, engine: EngineProxy) {...} // ... methods from the TracePlugin interface go here ... // } // ... which can then be passed around by class i.e. MyPlugin export interface PluginClass { // Instantiate the plugin. new (): Plugin; } // Describes a reference to a registered track. export interface TrackRef { // URI of the registered track. readonly uri: string; // A human readable name for this track - displayed in the track shell. readonly title: string; // Optional: Used to define default sort order for new traces. // Note: This will be deprecated soon in favour of tags & sort rules. readonly sortKey?: PrimaryTrackSortKey; // Optional: Add tracks to a group with this name. readonly groupName?: string; // Optional: Track key readonly key?: string; // Optional: Whether the track is pinned readonly isPinned?: boolean; } // A predicate for selecting a subset of tracks. export type TrackPredicate = (info: TrackDescriptor) => boolean; // Describes a reference to a group of tracks. export interface GroupRef { // A human readable name for this track group. displayName: string; // True if the track is open else false. collapsed: boolean; } // A predicate for selecting a subset of groups. export type GroupPredicate = (info: GroupRef) => boolean; // Plugins can be class refs or concrete plugin implementations. export type PluginFactory = PluginClass | Plugin; export interface PluginDescriptor { // A unique string for your plugin. To ensure the name is unique you // may wish to use a URL with reversed components in the manner of // Java package names. pluginId: string; // The plugin factory used to instantiate the plugin object, or if this is // an actual plugin implementation, it's just used as-is. plugin: PluginFactory; }