diff --git a/ui/src/common/conversion_jobs.ts b/ui/src/common/conversion_jobs.ts deleted file mode 100644 index 6805bc71f0..0000000000 --- a/ui/src/common/conversion_jobs.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2021 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. - -export enum ConversionJobStatus { - InProgress = 'InProgress', - NotRunning = 'NotRunning', -} - -export type ConversionJobName = - | 'convert_systrace' - | 'convert_json' - | 'open_in_legacy' - | 'convert_pprof' - | 'create_permalink'; - -export interface ConversionJobStatusUpdate { - jobName: ConversionJobName; - jobStatus: ConversionJobStatus; -} diff --git a/ui/src/core_plugins/commands/index.ts b/ui/src/core_plugins/commands/index.ts index f539cd38b1..4170338581 100644 --- a/ui/src/core_plugins/commands/index.ts +++ b/ui/src/core_plugins/commands/index.ts @@ -347,10 +347,9 @@ async function openWithLegacyUi(file: File) { 'Open trace in Legacy UI', ); if (await isLegacyTrace(file)) { - openFileWithLegacyTraceViewer(file); - return; + return await openFileWithLegacyTraceViewer(file); } - openInOldUIWithSizeCheck(file); + return await openInOldUIWithSizeCheck(file); } export const plugin: PluginDescriptor = { diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts index 347bb918a4..063dd32015 100644 --- a/ui/src/frontend/globals.ts +++ b/ui/src/frontend/globals.ts @@ -16,10 +16,6 @@ import {assertExists} from '../base/logging'; import {createStore, Store} from '../base/store'; import {Actions, DeferredAction} from '../common/actions'; import {CommandManagerImpl} from '../core/command_manager'; -import { - ConversionJobName, - ConversionJobStatus, -} from '../common/conversion_jobs'; import {createEmptyState} from '../common/empty_state'; import {State} from '../common/state'; import {setPerfHooks} from '../core/perf'; @@ -49,7 +45,6 @@ class Globals { private _trackDataStore?: TrackDataStore = undefined; private _bufferUsage?: number = undefined; private _recordingLog?: string = undefined; - private _jobStatus?: Map = undefined; httpRpcState: HttpRpcState = {connected: false}; showPanningHint = false; permalinkHash?: string; @@ -153,26 +148,6 @@ class Globals { return this._recordingLog; } - getConversionJobStatus(name: ConversionJobName): ConversionJobStatus { - return this.getJobStatusMap().get(name) ?? ConversionJobStatus.NotRunning; - } - - setConversionJobStatus(name: ConversionJobName, status: ConversionJobStatus) { - const map = this.getJobStatusMap(); - if (status === ConversionJobStatus.NotRunning) { - map.delete(name); - } else { - map.set(name, status); - } - } - - private getJobStatusMap(): Map { - if (!this._jobStatus) { - this._jobStatus = new Map(); - } - return this._jobStatus; - } - setBufferUsage(bufferUsage: number) { this._bufferUsage = bufferUsage; } diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts index 324671341b..04668f0042 100644 --- a/ui/src/frontend/legacy_trace_viewer.ts +++ b/ui/src/frontend/legacy_trace_viewer.ts @@ -172,16 +172,21 @@ function openBufferWithLegacyTraceViewer( }); } -export function openInOldUIWithSizeCheck(trace: Blob) { +export async function openInOldUIWithSizeCheck(trace: Blob): Promise { // Perfetto traces smaller than 50mb can be safely opened in the legacy UI. if (trace.size < 1024 * 1024 * 50) { - convertToJson(trace, openBufferWithLegacyTraceViewer); - return; + return await convertToJson(trace, openBufferWithLegacyTraceViewer); } // Give the user the option to truncate larger perfetto traces. const size = Math.round(trace.size / (1024 * 1024)); - showModal({ + + // If the user presses one of the buttons below, remember the promise that + // they trigger, so we await for it before returning. + let nextPromise: Promise | undefined; + const setNextPromise = (p: Promise) => (nextPromise = p); + + await showModal({ title: 'Legacy UI may fail to open this trace', content: m( 'div', @@ -206,30 +211,38 @@ export function openInOldUIWithSizeCheck(trace: Blob) { buttons: [ { text: 'Open full trace (not recommended)', - action: () => convertToJson(trace, openBufferWithLegacyTraceViewer), + action: () => + setNextPromise(convertToJson(trace, openBufferWithLegacyTraceViewer)), }, { text: 'Open beginning of trace', action: () => - convertToJson( - trace, - openBufferWithLegacyTraceViewer, - /* truncate*/ 'start', + setNextPromise( + convertToJson( + trace, + openBufferWithLegacyTraceViewer, + /* truncate*/ 'start', + ), ), }, { text: 'Open end of trace', primary: true, action: () => - convertToJson( - trace, - openBufferWithLegacyTraceViewer, - /* truncate*/ 'end', + setNextPromise( + convertToJson( + trace, + openBufferWithLegacyTraceViewer, + /* truncate*/ 'end', + ), ), }, ], }); - return; + // nextPromise is undefined if the user just dimisses the dialog with ESC. + if (nextPromise !== undefined) { + await nextPromise; + } } // TraceViewer method that we wire up to trigger the file load. diff --git a/ui/src/frontend/permalink.ts b/ui/src/frontend/permalink.ts index 959034703f..17ecb8ff64 100644 --- a/ui/src/frontend/permalink.ts +++ b/ui/src/frontend/permalink.ts @@ -15,7 +15,6 @@ import m from 'mithril'; import {assertExists} from '../base/logging'; import {Actions} from '../common/actions'; -import {ConversionJobStatus} from '../common/conversion_jobs'; import { JsonSerialize, parseAppState, @@ -28,10 +27,6 @@ import { GcsUploader, } from '../common/gcs_uploader'; import {globals} from './globals'; -import { - publishConversionJobStatusUpdate, - publishPermalinkHash, -} from './publish'; import { SERIALIZED_STATE_VERSION, SerializedAppState, @@ -40,6 +35,7 @@ import {z} from 'zod'; import {showModal} from '../widgets/modal'; import {AppImpl} from '../core/app_impl'; import {Router} from '../core/router'; +import {publishPermalinkHash} from './publish'; // Permalink serialization has two layers: // 1. Serialization of the app state (state_serialization.ts): @@ -76,21 +72,8 @@ export interface PermalinkOptions { } export async function createPermalink(opts: PermalinkOptions): Promise { - const jobName = 'create_permalink'; - publishConversionJobStatusUpdate({ - jobName, - jobStatus: ConversionJobStatus.InProgress, - }); - - try { - const hash = await createPermalinkInternal(opts); - publishPermalinkHash(hash); - } finally { - publishConversionJobStatusUpdate({ - jobName, - jobStatus: ConversionJobStatus.NotRunning, - }); - } + const hash = await createPermalinkInternal(opts); + publishPermalinkHash(hash); } // Returns the file name, not the full url (i.e. the name of the GCS object). diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts index 5eff236996..26e0a4ceed 100644 --- a/ui/src/frontend/publish.ts +++ b/ui/src/frontend/publish.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {ConversionJobStatusUpdate} from '../common/conversion_jobs'; import {raf} from '../core/raf_scheduler'; import {HttpRpcState} from '../trace_processor/http_rpc_engine'; import {globals} from './globals'; @@ -27,13 +26,6 @@ export function publishHttpRpcState(httpRpcState: HttpRpcState) { raf.scheduleFullRedraw(); } -export function publishConversionJobStatusUpdate( - job: ConversionJobStatusUpdate, -) { - globals.setConversionJobStatus(job.jobName, job.jobStatus); - globals.publishRedraw(); -} - export function publishBufferUsage(args: {percentage: number}) { globals.setBufferUsage(args.percentage); globals.publishRedraw(); diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts index 0218d6526b..f5980c6734 100644 --- a/ui/src/frontend/sidebar.ts +++ b/ui/src/frontend/sidebar.ts @@ -17,7 +17,6 @@ import {assertExists, assertTrue} from '../base/logging'; import {isString} from '../base/object_utils'; import {getCurrentChannel} from '../core/channels'; import {TRACE_SUFFIX} from '../common/constants'; -import {ConversionJobStatus} from '../common/conversion_jobs'; import { disableMetatracingAndGetTrace, enableMetatracing, @@ -47,7 +46,6 @@ import {formatHotkey} from '../base/hotkeys'; import {SidebarMenuItem} from '../public/sidebar'; import {AppImpl} from '../core/app_impl'; import {Trace} from '../public/trace'; -import {Router} from '../core/router'; const GITILES_URL = 'https://android.googlesource.com/platform/external/perfetto'; @@ -112,10 +110,9 @@ function shouldShowHiringBanner(): boolean { interface SectionItem { t: string; - a: string | ((e: Event) => void); + a: string | (() => void | Promise); i: string; - title?: string; - isPending?: () => boolean; + tooltip?: string; isVisible?: () => boolean; internalUserOnly?: boolean; checkDownloadDisabled?: boolean; @@ -150,10 +147,7 @@ function insertSidebarMenuitems( : cmd.name; return { t: cmd.name, - a: (e: Event) => { - e.preventDefault(); - cmd.callback(); - }, + a: cmd.callback, i: item.icon, title, }; @@ -170,18 +164,18 @@ function getSections(trace?: Trace): Section[] { ...insertSidebarMenuitems('navigation'), { t: 'Record new trace', - a: (e: Event) => navigateToPage(e, 'record'), + a: '#!/record', i: 'fiber_smart_record', }, { t: 'Widgets', - a: (e: Event) => navigateToPage(e, 'widgets'), + a: '#!/widgets', i: 'widgets', isVisible: () => WIDGETS_PAGE_IN_NAV_FLAG.get(), }, { t: 'Plugins', - a: (e: Event) => navigateToPage(e, 'plugins'), + a: '#!/plugins', i: 'extension', isVisible: () => PLUGINS_PAGE_IN_NAV_FLAG.get(), }, @@ -194,59 +188,48 @@ function getSections(trace?: Trace): Section[] { hideIfNoTraceLoaded: true, appendOpenedTraceTitle: true, items: [ - { - t: 'Show timeline', - a: (e: Event) => navigateToPage(e, 'viewer'), - i: 'line_style', - }, + {t: 'Show timeline', a: '#!/viewer', i: 'line_style'}, { t: 'Share', - a: handleShareTrace, + a: shareTrace, i: 'share', internalUserOnly: true, - isPending: () => - globals.getConversionJobStatus('create_permalink') === - ConversionJobStatus.InProgress, }, { t: 'Download', - a: (e: Event) => trace && downloadTrace(e, trace), + a: () => { + if (trace) { + downloadTrace(trace); + } + }, i: 'file_download', checkDownloadDisabled: true, }, { t: 'Query (SQL)', - a: (e: Event) => navigateToPage(e, 'query'), + a: '#!/query', i: 'database', }, { t: 'Explore', - a: (e: Event) => navigateToPage(e, 'explore'), + a: '#!/explore', i: 'data_exploration', isVisible: () => EXPLORE_PAGE_IN_NAV_FLAG.get(), }, { t: 'Insights', - a: (e: Event) => navigateToPage(e, 'insights'), + a: '#!/insights', i: 'insights', isVisible: () => INSIGHTS_PAGE_IN_NAV_FLAG.get(), }, { t: 'Viz', - a: (e: Event) => navigateToPage(e, 'viz'), + a: '#!/viz', i: 'area_chart', isVisible: () => VIZ_PAGE_IN_NAV_FLAG.get(), }, - { - t: 'Metrics', - a: (e: Event) => navigateToPage(e, 'metrics'), - i: 'speed', - }, - { - t: 'Info and stats', - a: (e: Event) => navigateToPage(e, 'info'), - i: 'info', - }, + {t: 'Metrics', a: '#!/metrics', i: 'speed'}, + {t: 'Info and stats', a: '#!/info', i: 'info'}, ], }, @@ -260,17 +243,11 @@ function getSections(trace?: Trace): Section[] { t: 'Switch to legacy UI', a: openCurrentTraceWithOldUI, i: 'filter_none', - isPending: () => - globals.getConversionJobStatus('open_in_legacy') === - ConversionJobStatus.InProgress, }, { t: 'Convert to .json', a: convertTraceToJson, i: 'file_download', - isPending: () => - globals.getConversionJobStatus('convert_json') === - ConversionJobStatus.InProgress, checkDownloadDisabled: true, }, @@ -279,9 +256,6 @@ function getSections(trace?: Trace): Section[] { a: convertTraceToSystrace, i: 'file_download', isVisible: () => Boolean(trace?.traceInfo.hasFtrace), - isPending: () => - globals.getConversionJobStatus('convert_systrace') === - ConversionJobStatus.InProgress, checkDownloadDisabled: true, }, ], @@ -299,13 +273,9 @@ function getSections(trace?: Trace): Section[] { expanded: true, summary: 'Documentation & Bugs', items: [ - {t: 'Keyboard shortcuts', a: openHelp, i: 'help'}, + {t: 'Keyboard shortcuts', a: toggleHelp, i: 'help'}, {t: 'Documentation', a: 'https://perfetto.dev/docs', i: 'find_in_page'}, - { - t: 'Flags', - a: (e: Event) => navigateToPage(e, 'flags'), - i: 'emoji_flags', - }, + {t: 'Flags', a: '#!/flags', i: 'emoji_flags'}, { t: 'Report a bug', a: getBugReportUrl(), @@ -315,13 +285,13 @@ function getSections(trace?: Trace): Section[] { ? [ { t: 'Record metatrace', - a: (e: Event) => recordMetatrace(e, trace.engine), + a: () => recordMetatrace(trace.engine), i: 'fiber_smart_record', checkMetatracingDisabled: true, }, { t: 'Finalise metatrace', - a: (e: Event) => finaliseMetatrace(e, trace.engine), + a: () => finaliseMetatrace(trace.engine), i: 'file_download', checkMetatracingEnabled: true, }, @@ -332,11 +302,6 @@ function getSections(trace?: Trace): Section[] { ]; } -function openHelp(e: Event) { - e.preventDefault(); - toggleHelp(); -} - function downloadTraceFromUrl(url: string): Promise { return m.request({ method: 'GET', @@ -365,69 +330,40 @@ export async function getCurrentTrace(): Promise { } else if (src.type === 'FILE') { return src.file; } else if (src.type === 'URL') { - return downloadTraceFromUrl(src.url); + return await downloadTraceFromUrl(src.url); } else { throw new Error(`Loading to catapult from source with type ${src.type}`); } } -function openCurrentTraceWithOldUI(e: Event) { - e.preventDefault(); +async function openCurrentTraceWithOldUI(): Promise { assertTrue(isTraceLoaded()); AppImpl.instance.analytics.logEvent( 'Trace Actions', 'Open current trace in legacy UI', ); if (!isTraceLoaded()) return; - getCurrentTrace() - .then((file) => { - openInOldUIWithSizeCheck(file); - }) - .catch((error) => { - throw new Error(`Failed to get current trace ${error}`); - }); + const file = await getCurrentTrace(); + await openInOldUIWithSizeCheck(file); } -function convertTraceToSystrace(e: Event) { - e.preventDefault(); +async function convertTraceToSystrace(): Promise { assertTrue(isTraceLoaded()); AppImpl.instance.analytics.logEvent('Trace Actions', 'Convert to .systrace'); if (!isTraceLoaded()) return; - getCurrentTrace() - .then((file) => { - convertTraceToSystraceAndDownload(file); - }) - .catch((error) => { - throw new Error(`Failed to get current trace ${error}`); - }); + const file = await getCurrentTrace(); + await convertTraceToSystraceAndDownload(file); } -function convertTraceToJson(e: Event) { - e.preventDefault(); +async function convertTraceToJson(): Promise { assertTrue(isTraceLoaded()); AppImpl.instance.analytics.logEvent('Trace Actions', 'Convert to .json'); if (!isTraceLoaded()) return; - getCurrentTrace() - .then((file) => { - convertTraceToJsonAndDownload(file); - }) - .catch((error) => { - throw new Error(`Failed to get current trace ${error}`); - }); -} - -function navigateToPage(e: Event, pageName: string) { - e.preventDefault(); - Router.navigate(`#!/${pageName}`); + const file = await getCurrentTrace(); + await convertTraceToJsonAndDownload(file); } -function handleShareTrace(e: Event) { - e.preventDefault(); - shareTrace(); -} - -function downloadTrace(e: Event, trace: Trace) { - e.preventDefault(); +function downloadTrace(trace: Trace) { if (!isDownloadable() || !isTraceLoaded()) return; AppImpl.instance.analytics.logEvent('Trace Actions', 'Download trace'); @@ -467,8 +403,7 @@ function highPrecisionTimersAvailable(): boolean { ); } -function recordMetatrace(e: Event, engine: Engine) { - e.preventDefault(); +function recordMetatrace(engine: Engine) { AppImpl.instance.analytics.logEvent('Trace Actions', 'Record metatrace'); if (!highPrecisionTimersAvailable()) { @@ -505,8 +440,7 @@ Alternatively, connect to a trace_processor_shell --httpd instance. } } -async function finaliseMetatrace(e: Event, engine: Engine) { - e.preventDefault(); +async function finaliseMetatrace(engine: Engine) { AppImpl.instance.analytics.logEvent('Trace Actions', 'Finalise metatrace'); const jsEvents = disableMetatracingAndGetTrace(); @@ -692,10 +626,57 @@ class HiringBanner implements m.ClassComponent { export class Sidebar implements m.ClassComponent { private _redrawWhileAnimating = new Animation(() => raf.scheduleFullRedraw()); + private _asyncJobPending = new Set(); + private _onClickHandlers = new Map(); + view({attrs}: m.CVnode) { if (AppImpl.instance.sidebar.sidebarHidden) return null; + + // The code below iterates through the sections and SectionActions provided + // by getSections() and creates the onClick handlers for the items where + // a (async)function is provided. + // We do it in view() and not in the constructor because new sidebar items + // can be added later by plugins. + // What we want to achieve here is the following: + // - We want to allow plugins that contribute to the sidebar to just specify + // either string URLs or (async) functions as actions for a sidebar menu. + // - When they specify an async function, we want to render a spinner, next + // to the menu item, until the promise is resolved. + // - [Minor] we want to call e.preventDefault() to override the behaviour of + // the which gets rendered for accessibility reasons. + const sections = getSections(attrs.trace); + for (const section of sections) { + for (const item of section.items) { + const itemId = item.t; + // We call this on every render pass. Don't re-create wrappers on each + // render cycle if we did it already as that is wasteful. + if (this._onClickHandlers.has(itemId)) continue; + + const itemAction = item.a; + + // item.a can be either a function or a URL. In the latter case, we + // don't need to generate any onclick handler. + if (typeof itemAction !== 'function') continue; + const onClickHandler = (e: Event) => { + e.preventDefault(); // Make the a no-op. + const res = itemAction(); + if (!(res instanceof Promise)) return; + if (this._asyncJobPending.has(itemId)) { + return; // Don't queue up another action if not yet finished. + } + this._asyncJobPending.add(itemId); + raf.scheduleFullRedraw(); + res.finally(() => { + this._asyncJobPending.delete(itemId); + raf.scheduleFullRedraw(); + }); + }; + this._onClickHandlers.set(itemId, onClickHandler); + } + } + const vdomSections = []; - for (const section of getSections(attrs.trace)) { + for (const section of sections) { if (section.hideIfNoTraceLoaded && !isTraceLoaded()) continue; const vdomItems = []; for (const item of section.items) { @@ -704,14 +685,14 @@ export class Sidebar implements m.ClassComponent { } let css = ''; let attrs = { - onclick: typeof item.a === 'function' ? item.a : null, + onclick: this._onClickHandlers.get(item.t), href: isString(item.a) ? item.a : '#', - target: isString(item.a) ? '_blank' : null, + target: isString(item.a) && !item.a.startsWith('#') ? '_blank' : null, disabled: false, id: item.t.toLowerCase().replace(/[^\w]/g, '_'), }; - if (item.isPending && item.isPending()) { - attrs.onclick = (e) => e.preventDefault(); + + if (this._asyncJobPending.has(item.t)) { css = '.pending'; } if (item.internalUserOnly && !globals.isInternalUser) { @@ -739,7 +720,7 @@ export class Sidebar implements m.ClassComponent { } if (item.checkDownloadDisabled && !isDownloadable()) { attrs = { - onclick: (e) => { + onclick: (e: Event) => { e.preventDefault(); alert('Can not download external trace.'); }, @@ -754,7 +735,7 @@ export class Sidebar implements m.ClassComponent { 'li', m( `a${css}`, - {...attrs, title: item.title}, + {...attrs, title: item.tooltip}, m('i.material-icons', item.i), item.t, ), diff --git a/ui/src/frontend/trace_attrs.ts b/ui/src/frontend/trace_attrs.ts index b996265503..dd0f1ab3b6 100644 --- a/ui/src/frontend/trace_attrs.ts +++ b/ui/src/frontend/trace_attrs.ts @@ -39,7 +39,7 @@ export function isDownloadable() { return true; } -export function shareTrace() { +export async function shareTrace() { const traceSource = assertExists(AppImpl.instance.trace?.traceInfo.source); const traceUrl = (traceSource as TraceUrlSource).url ?? ''; @@ -74,7 +74,7 @@ export function shareTrace() { ); if (result) { AppImpl.instance.analytics.logEvent('Trace Actions', 'Create permalink'); - createPermalink({mode: 'APP_STATE'}); + return await createPermalink({mode: 'APP_STATE'}); } } diff --git a/ui/src/frontend/trace_converter.ts b/ui/src/frontend/trace_converter.ts index be518d86df..f220120146 100644 --- a/ui/src/frontend/trace_converter.ts +++ b/ui/src/frontend/trace_converter.ts @@ -13,20 +13,17 @@ // limitations under the License. import {download} from '../base/clipboard'; +import {defer} from '../base/deferred'; import {ErrorDetails} from '../base/logging'; import {utf8Decode} from '../base/string_utils'; import {time} from '../base/time'; -import { - ConversionJobName, - ConversionJobStatus, -} from '../common/conversion_jobs'; import {AppImpl} from '../core/app_impl'; import {maybeShowErrorDialog} from './error_dialog'; import {globals} from './globals'; type Args = | UpdateStatusArgs - | UpdateJobStatusArgs + | JobCompletedArgs | DownloadFileArgs | OpenTraceInLegacyArgs | ErrorArgs; @@ -36,10 +33,8 @@ interface UpdateStatusArgs { status: string; } -interface UpdateJobStatusArgs { - kind: 'updateJobStatus'; - name: ConversionJobName; - status: ConversionJobStatus; +interface JobCompletedArgs { + kind: 'jobCompleted'; } interface DownloadFileArgs { @@ -64,16 +59,18 @@ type OpenTraceInLegacyCallback = ( size: number, ) => void; -function makeWorkerAndPost( +async function makeWorkerAndPost( msg: unknown, openTraceInLegacy?: OpenTraceInLegacyCallback, ) { + const promise = defer(); + function handleOnMessage(msg: MessageEvent): void { const args: Args = msg.data; if (args.kind === 'updateStatus') { AppImpl.instance.omnibox.showStatusMessage(args.status); - } else if (args.kind === 'updateJobStatus') { - globals.setConversionJobStatus(args.name, args.status); + } else if (args.kind === 'jobCompleted') { + promise.resolve(); } else if (args.kind === 'downloadFile') { download(new File([new Blob([args.buffer])], args.name)); } else if (args.kind === 'openTraceInLegacy') { @@ -89,18 +86,19 @@ function makeWorkerAndPost( const worker = new Worker(globals.root + 'traceconv_bundle.js'); worker.onmessage = handleOnMessage; worker.postMessage(msg); + return promise; } -export function convertTraceToJsonAndDownload(trace: Blob) { - makeWorkerAndPost({ +export function convertTraceToJsonAndDownload(trace: Blob): Promise { + return makeWorkerAndPost({ kind: 'ConvertTraceAndDownload', trace, format: 'json', }); } -export function convertTraceToSystraceAndDownload(trace: Blob) { - makeWorkerAndPost({ +export function convertTraceToSystraceAndDownload(trace: Blob): Promise { + return makeWorkerAndPost({ kind: 'ConvertTraceAndDownload', trace, format: 'systrace', @@ -111,8 +109,8 @@ export function convertToJson( trace: Blob, openTraceInLegacy: OpenTraceInLegacyCallback, truncate?: 'start' | 'end', -) { - makeWorkerAndPost( +): Promise { + return makeWorkerAndPost( { kind: 'ConvertTraceAndOpenInLegacy', trace, @@ -126,8 +124,8 @@ export function convertTraceToPprofAndDownload( trace: Blob, pid: number, ts: time, -) { - makeWorkerAndPost({ +): Promise { + return makeWorkerAndPost({ kind: 'ConvertTraceToPprof', trace, pid, diff --git a/ui/src/traceconv/index.ts b/ui/src/traceconv/index.ts index 918192032e..cc721ba467 100644 --- a/ui/src/traceconv/index.ts +++ b/ui/src/traceconv/index.ts @@ -20,10 +20,6 @@ import { reportError, } from '../base/logging'; import {time} from '../base/time'; -import { - ConversionJobName, - ConversionJobStatus, -} from '../common/conversion_jobs'; import traceconv from '../gen/traceconv'; const selfWorker = self as {} as Worker; @@ -44,12 +40,8 @@ function updateStatus(status: string) { }); } -function updateJobStatus(name: ConversionJobName, status: ConversionJobStatus) { - selfWorker.postMessage({ - kind: 'updateJobStatus', - name, - status, - }); +function notifyJobCompleted() { + selfWorker.postMessage({kind: 'jobCompleted'}); } function downloadFile(buffer: Uint8Array, name: string) { @@ -131,8 +123,6 @@ async function ConvertTraceAndDownload( format: Format, truncate?: 'start' | 'end', ): Promise { - const jobName = format === 'json' ? 'convert_json' : 'convert_systrace'; - updateJobStatus(jobName, ConversionJobStatus.InProgress); const outPath = '/trace.json'; const args: string[] = [format]; if (truncate !== undefined) { @@ -145,7 +135,7 @@ async function ConvertTraceAndDownload( downloadFile(fsNodeToBuffer(fsNode), `trace.${format}`); module.FS.unlink(outPath); } finally { - updateJobStatus(jobName, ConversionJobStatus.NotRunning); + notifyJobCompleted(); } } @@ -168,8 +158,6 @@ async function ConvertTraceAndOpenInLegacy( trace: Blob, truncate?: 'start' | 'end', ) { - const jobName = 'open_in_legacy'; - updateJobStatus(jobName, ConversionJobStatus.InProgress); const outPath = '/trace.json'; const args: string[] = ['json']; if (truncate !== undefined) { @@ -185,7 +173,7 @@ async function ConvertTraceAndOpenInLegacy( openTraceInLegacy(buffer); module.FS.unlink(outPath); } finally { - updateJobStatus(jobName, ConversionJobStatus.NotRunning); + notifyJobCompleted(); } } @@ -204,8 +192,6 @@ function isConvertTraceToPprof(msg: Args): msg is ConvertTraceToPprofArgs { } async function ConvertTraceToPprof(trace: Blob, pid: number, ts: time) { - const jobName = 'convert_pprof'; - updateJobStatus(jobName, ConversionJobStatus.InProgress); const args = [ 'profile', `--pid`, @@ -232,7 +218,7 @@ async function ConvertTraceToPprof(trace: Blob, pid: number, ts: time) { downloadFile(fsNodeToBuffer(fileNode), fileName); } } finally { - updateJobStatus(jobName, ConversionJobStatus.NotRunning); + notifyJobCompleted(); } }