Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion ui/src/core/selection_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {assertTrue, assertUnreachable} from '../base/logging';
import {assertExists, assertTrue, assertUnreachable} from '../base/logging';
import {
Selection,
Area,
Expand All @@ -35,6 +35,7 @@ import {SerializedSelection} from './state_serialization_schema';
import {showModal} from '../widgets/modal';
import {NUM, SqlValue, UNKNOWN} from '../trace_processor/query_result';
import {SourceDataset, UnionDataset} from '../trace_processor/dataset';
import {Trace} from '../public/trace';
import {Track} from '../public/track';
import {TimelineImpl} from './timeline';
import {HighPrecisionTime} from '../base/high_precision_time';
Expand All @@ -60,6 +61,7 @@ export class SelectionManagerImpl implements SelectionManager {
Selection,
SelectionDetailsPanel
>();
private _trace?: Trace;
public readonly areaSelectionTabs: AreaSelectionTab[] = [];

constructor(
Expand All @@ -71,6 +73,15 @@ export class SelectionManagerImpl implements SelectionManager {
private onSelectionChange: (s: Selection, opts: SelectionOpts) => void,
) {}

get trace(): Trace {
return assertExists(this._trace);
}

setTrace(trace: Trace): void {
assertTrue(this._trace === undefined);
this._trace = trace;
}

clearSelection(): void {
this.setSelection({kind: 'empty'});
}
Expand Down Expand Up @@ -149,6 +160,7 @@ export class SelectionManagerImpl implements SelectionManager {
}
} catch (ex) {
showModal({
owner: this.trace,
title: 'Failed to restore the selected event',
content: m(
'div',
Expand Down
5 changes: 5 additions & 0 deletions ui/src/core/trace_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ export class TraceImpl implements Trace {
this.traceCtx = ctx;
const traceUnloadTrash = ctx.trash;

if (pluginId === CORE_PLUGIN_ID) {
// Inject myself into the selection manager so that it can target modal dialogs
ctx.selectionMgr.setTrace(this);
}

const childOmnibox = appImpl.omnibox.childFor(ctx);
traceUnloadTrash.use(childOmnibox);
this.omniboxMgr = childOmnibox;
Expand Down
1 change: 1 addition & 0 deletions ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ export default class CoreCommands implements PerfettoPlugin {
const json = localStorage.getItem(QUICKSAVE_LOCALSTORAGE_KEY);
if (json === null) {
showModal({
owner: ctx,
title: 'Nothing saved in the quicksave slot',
buttons: [{text: 'Dismiss'}],
});
Expand Down
4 changes: 3 additions & 1 deletion ui/src/frontend/help_modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ import {
} from '../base/keyboard_layout_map';
import {KeyMapping} from './viewer_page/wasd_navigation_handler';
import {raf} from '../core/raf_scheduler';
import {App} from '../public/app';

export function toggleHelp() {
export function toggleHelp(app: App) {
AppImpl.instance.analytics.logEvent('User Actions', 'Show help');
return showModal({
owner: app,
title: 'Perfetto Help',
content: () => m(KeyMappingsHelp),
});
Expand Down
4 changes: 3 additions & 1 deletion ui/src/frontend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {checkHttpRpcConnection} from './rpc_http_dialog';
import {maybeOpenTraceFromRoute} from './trace_url_handler';
import {renderViewerPage} from './viewer_page/viewer_page';
import {HttpRpcEngine} from '../trace_processor/http_rpc_engine';
import {showModal} from '../widgets/modal';
import {setDefaultOwnerFunction, showModal} from '../widgets/modal';
import {IdleDetector} from './idle_detector';
import {IdleDetectorWindow} from './idle_detector_interface';
import {AppImpl} from '../core/app_impl';
Expand Down Expand Up @@ -269,6 +269,8 @@ function main() {
enforceStartupCommandAllowlistSetting,
});

setDefaultOwnerFunction(() => AppImpl.instance.trace ?? AppImpl.instance);

// Load the css. The load is asynchronous and the CSS is not ready by the time
// appendChild returns.
const cssLoadPromise = defer<void>();
Expand Down
2 changes: 1 addition & 1 deletion ui/src/frontend/post_message_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function postMessageHandler(messageEvent: MessageEvent) {
}

if (messageEvent.data === 'SHOW-HELP') {
toggleHelp();
toggleHelp(AppImpl.instance.trace ?? AppImpl.instance);
return;
}

Expand Down
25 changes: 15 additions & 10 deletions ui/src/frontend/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ function downloadTrace(trace: TraceImpl) {
}
}

function recordMetatrace(engine: Engine) {
function recordMetatrace(trace: TraceImpl) {
AppImpl.instance.analytics.logEvent('Trace Actions', 'Record metatrace');
const engine = trace.engine;

const highPrecisionTimersAvailable =
window.crossOriginIsolated || engine.mode === 'HTTP_RPC';
Expand All @@ -146,6 +147,7 @@ Note that events under timer precision (1ms) will dropped.
Alternatively, connect to a trace_processor_shell --httpd instance.
`;
showModal({
owner: trace,
title: `Trace processor doesn't have high-precision timers`,
content: m('.pf-modal-pre', PROMPT),
buttons: [
Expand All @@ -170,8 +172,10 @@ Alternatively, connect to a trace_processor_shell --httpd instance.
}
}

async function toggleMetatrace(e: Engine) {
return isMetatracingEnabled() ? finaliseMetatrace(e) : recordMetatrace(e);
async function toggleMetatrace(trace: TraceImpl) {
return isMetatracingEnabled()
? finaliseMetatrace(trace.engine)
: recordMetatrace(trace);
}

async function finaliseMetatrace(engine: Engine) {
Expand Down Expand Up @@ -243,8 +247,8 @@ class EngineRPCWidget implements m.ClassComponent<OptionalTraceImplAttrs> {
}
}

const ServiceWorkerWidget: m.Component = {
view() {
const ServiceWorkerWidget: m.Component<OptionalTraceImplAttrs> = {
view({attrs}) {
let cssClass = '';
let title = 'Service Worker: ';
let label = 'N/A';
Expand Down Expand Up @@ -275,6 +279,7 @@ const ServiceWorkerWidget: m.Component = {
return;
}
showModal({
owner: attrs.trace ?? AppImpl.instance,
title: 'Disable service worker?',
content: m(
'div',
Expand Down Expand Up @@ -324,7 +329,7 @@ class SidebarFooter implements m.ClassComponent<OptionalTraceImplAttrs> {
return m(
'.pf-sidebar__footer',
m(EngineRPCWidget, attrs),
m(ServiceWorkerWidget),
m(ServiceWorkerWidget, {trace: attrs.trace}),
m(
'.pf-sidebar__version',
m(
Expand Down Expand Up @@ -547,22 +552,22 @@ const traceItemsRegistered = new WeakSet<TraceImpl>();
function registerMenuItems(trace: TraceImpl | undefined) {
if (!globalItemsRegistered) {
globalItemsRegistered = true;
registerGlobalSidebarEntries();
registerGlobalSidebarEntries(trace);
}
if (trace !== undefined && !traceItemsRegistered.has(trace)) {
traceItemsRegistered.add(trace);
registerTraceMenuItems(trace);
}
}

function registerGlobalSidebarEntries() {
function registerGlobalSidebarEntries(trace: TraceImpl | undefined) {
const app = AppImpl.instance;
// TODO(primiano): The Open file / Open with legacy entries are registered by
// the 'perfetto.CoreCommands' plugins. Make things consistent.
app.sidebar.addMenuItem({
section: 'support',
text: 'Keyboard shortcuts',
action: toggleHelp,
action: () => toggleHelp(trace ?? app),
icon: 'help',
});
app.sidebar.addMenuItem({
Expand Down Expand Up @@ -647,7 +652,7 @@ function registerTraceMenuItems(trace: TraceImpl) {
sortOrder: 5,
text: () =>
isMetatracingEnabled() ? 'Finalize metatrace' : 'Record metatrace',
action: () => toggleMetatrace(trace.engine),
action: () => toggleMetatrace(trace),
icon: () => (isMetatracingEnabled() ? 'download' : 'fiber_smart_record'),
});
}
Expand Down
4 changes: 4 additions & 0 deletions ui/src/frontend/trace_share_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export async function shareTrace(trace: TraceImpl) {
const traceUrl = await uploadTraceBlob(trace);
const hash = await createPermalink(trace, traceUrl);
showModal({
owner: trace,
title: 'Permalink',
content: m(CopyableLink, {
url: `${self.location.origin}/#!/?s=${hash}`,
Expand All @@ -69,13 +70,15 @@ export async function shareTrace(trace: TraceImpl) {
const hash = await createPermalink(trace, undefined);
const urlWithHash = traceUrl.replace(STATE_HASH_PLACEHOLDER, hash);
showModal({
owner: trace,
title: 'Permalink',
content: m(CopyableLink, {url: urlWithHash}),
});
}
} else {
// Trace is not sharable, has a URL, but no placeholder.
showModal({
owner: trace,
title: 'Cannot create permalink from external trace',
content: m(
'',
Expand All @@ -94,6 +97,7 @@ export async function shareTrace(trace: TraceImpl) {
// Trace is not sharable and has no URL. Nothing we can do. Just tell the
// user.
showModal({
owner: trace,
title: 'Cannot create permalink',
content: m(
'p',
Expand Down
9 changes: 5 additions & 4 deletions ui/src/frontend/ui_main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class UiMainPerTrace implements m.ClassComponent<UiMainPerTraceAttrs> {
{
id: 'dev.perfetto.ShowHelp',
name: 'Show help',
callback: () => toggleHelp(),
callback: () => toggleHelp(this.preferredApp),
defaultHotkey: '?',
},
];
Expand All @@ -103,7 +103,7 @@ export class UiMainPerTrace implements m.ClassComponent<UiMainPerTraceAttrs> {
// commands or anything in this state as they will be useless.
if (trace === undefined) return;
document.title = `${trace.traceInfo.traceTitle || 'Trace'} - Perfetto UI`;
this.maybeShowJsonWarning();
this.maybeShowJsonWarning(this.preferredApp);
}

private renderOmnibox(): m.Children {
Expand Down Expand Up @@ -385,7 +385,7 @@ export class UiMainPerTrace implements m.ClassComponent<UiMainPerTraceAttrs> {
this.preferredApp.pages.renderPageForCurrentRoute(this.trace),
),
m(CookieConsent),
maybeRenderFullscreenModalDialog(),
maybeRenderFullscreenModalDialog(this.preferredApp),
showStatusBarFlag.get() && renderStatusBar(this.trace),
this.preferredApp.perfDebugging.renderPerfStats(),
]);
Expand Down Expand Up @@ -442,7 +442,7 @@ export class UiMainPerTrace implements m.ClassComponent<UiMainPerTraceAttrs> {
}
}

private async maybeShowJsonWarning() {
private async maybeShowJsonWarning(app: AppImpl | TraceImpl) {
// Show warning if the trace is in JSON format.
const isJsonTrace = this.trace?.traceInfo.traceType === 'json';
const SHOWN_JSON_WARNING_KEY = 'shownJsonWarning';
Expand All @@ -462,6 +462,7 @@ export class UiMainPerTrace implements m.ClassComponent<UiMainPerTraceAttrs> {
window.localStorage.setItem(SHOWN_JSON_WARNING_KEY, 'true');

showModal({
owner: app,
title: 'Warning',
content: m(
'div',
Expand Down
11 changes: 9 additions & 2 deletions ui/src/frontend/viewer_page/track_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,10 @@ export class TrackView {
scrollIntoView = true;
}

const modalOwner = this.trace;
function showTrackMoveErrorModal(msg: string) {
showModal({
owner: modalOwner,
title: 'Error',
content: msg,
buttons: [{text: 'OK'}],
Expand Down Expand Up @@ -612,7 +614,7 @@ const TrackPopupMenu = {
m(
MenuItem,
{label: 'Track details', icon: 'info'},
renderTrackDetailsMenu(attrs.node, attrs.descriptor),
renderTrackDetailsMenu(attrs.trace, attrs.node, attrs.descriptor),
),
m(MenuDivider),
m(
Expand Down Expand Up @@ -691,7 +693,11 @@ function copyToWorkspace(trace: Trace, node: TrackNode, ws?: Workspace) {
return ws;
}

function renderTrackDetailsMenu(node: TrackNode, descriptor?: Track) {
function renderTrackDetailsMenu(
trace: Trace,
node: TrackNode,
descriptor?: Track,
) {
const fullPath = node.fullPath.join(' \u2023 ');
const query = descriptor?.renderer.getDataset?.()?.query();

Expand Down Expand Up @@ -729,6 +735,7 @@ function renderTrackDetailsMenu(node: TrackNode, descriptor?: Track) {
{
onclick: () => {
showModal({
owner: trace,
title: 'Query for track',
content: () => m(CodeSnippet, {text: query, language: 'SQL'}),
});
Expand Down
16 changes: 9 additions & 7 deletions ui/src/plugins/dev.perfetto.CriticalPath/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,18 @@ function getFirstUtidOfSelectionOrVisibleWindow(trace: Trace): number {
return 0;
}

function showModalErrorAreaSelectionRequired() {
function showModalErrorAreaSelectionRequired(trace: Trace) {
showModal({
owner: trace,
title: 'Error: range selection required',
content:
'This command requires an area selection over a thread state track.',
});
}

function showModalErrorThreadStateRequired() {
function showModalErrorThreadStateRequired(trace: Trace) {
showModal({
owner: trace,
title: 'Error: thread state selection required',
content: 'This command requires a thread state slice to be selected.',
});
Expand Down Expand Up @@ -165,7 +167,7 @@ export default class implements PerfettoPlugin {
callback: async (utid?: Utid) => {
const thdInfo = await getThreadInfoForUtidOrSelection(ctx, utid);
if (thdInfo === undefined) {
return showModalErrorThreadStateRequired();
return showModalErrorThreadStateRequired(ctx);
}
ctx.engine
.query(`INCLUDE PERFETTO MODULE sched.thread_executing_span;`)
Expand Down Expand Up @@ -207,7 +209,7 @@ export default class implements PerfettoPlugin {
callback: async (utid?: Utid) => {
const thdInfo = await getThreadInfoForUtidOrSelection(ctx, utid);
if (thdInfo === undefined) {
return showModalErrorThreadStateRequired();
return showModalErrorThreadStateRequired(ctx);
}
ctx.engine
.query(
Expand Down Expand Up @@ -243,7 +245,7 @@ export default class implements PerfettoPlugin {
const trackUtid = getFirstUtidOfSelectionOrVisibleWindow(ctx);
const window = await getTimeSpanOfSelectionOrVisibleWindow(ctx);
if (trackUtid === 0) {
return showModalErrorAreaSelectionRequired();
return showModalErrorAreaSelectionRequired(ctx);
}
await ctx.engine.query(
`INCLUDE PERFETTO MODULE sched.thread_executing_span;`,
Expand Down Expand Up @@ -286,7 +288,7 @@ export default class implements PerfettoPlugin {
const trackUtid = getFirstUtidOfSelectionOrVisibleWindow(ctx);
const window = await getTimeSpanOfSelectionOrVisibleWindow(ctx);
if (trackUtid === 0) {
return showModalErrorAreaSelectionRequired();
return showModalErrorAreaSelectionRequired(ctx);
}
await ctx.engine.query(
`INCLUDE PERFETTO MODULE sched.thread_executing_span_with_slice;`,
Expand Down Expand Up @@ -321,7 +323,7 @@ export default class implements PerfettoPlugin {
const trackUtid = getFirstUtidOfSelectionOrVisibleWindow(ctx);
const window = await getTimeSpanOfSelectionOrVisibleWindow(ctx);
if (trackUtid === 0) {
return showModalErrorAreaSelectionRequired();
return showModalErrorAreaSelectionRequired(ctx);
}
addQueryResultsTab(ctx, {
query: `
Expand Down
2 changes: 1 addition & 1 deletion ui/src/plugins/dev.perfetto.ExplorePage/explore_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class ExplorePage implements m.ClassComponent<ExplorePageAttrs> {
return;
}

const selection = await modalForTableSelection(sqlModules);
const selection = await modalForTableSelection(trace, sqlModules);

if (selection) {
const newNode = new TableSourceNode({
Expand Down
Loading