// 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. import {assetSrc} from '../base/assets'; 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 {AppImpl} from '../core/app_impl'; import {maybeShowErrorDialog} from './error_dialog'; type Args = | UpdateStatusArgs | JobCompletedArgs | DownloadFileArgs | OpenTraceInLegacyArgs | ErrorArgs; interface UpdateStatusArgs { kind: 'updateStatus'; status: string; } interface JobCompletedArgs { kind: 'jobCompleted'; } interface DownloadFileArgs { kind: 'downloadFile'; buffer: Uint8Array; name: string; } interface OpenTraceInLegacyArgs { kind: 'openTraceInLegacy'; buffer: Uint8Array; } interface ErrorArgs { kind: 'error'; error: ErrorDetails; } type OpenTraceInLegacyCallback = ( name: string, data: ArrayBuffer | string, size: number, ) => void; 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 === 'jobCompleted') { promise.resolve(); } else if (args.kind === 'downloadFile') { download(new File([new Blob([args.buffer])], args.name)); } else if (args.kind === 'openTraceInLegacy') { const str = utf8Decode(args.buffer); openTraceInLegacy?.('trace.json', str, 0); } else if (args.kind === 'error') { maybeShowErrorDialog(args.error); } else { throw new Error(`Unhandled message ${JSON.stringify(args)}`); } } const worker = new Worker(assetSrc('traceconv_bundle.js')); worker.onmessage = handleOnMessage; worker.postMessage(msg); return promise; } export function convertTraceToJsonAndDownload(trace: Blob): Promise { return makeWorkerAndPost({ kind: 'ConvertTraceAndDownload', trace, format: 'json', }); } export function convertTraceToSystraceAndDownload(trace: Blob): Promise { return makeWorkerAndPost({ kind: 'ConvertTraceAndDownload', trace, format: 'systrace', }); } export function convertToJson( trace: Blob, openTraceInLegacy: OpenTraceInLegacyCallback, truncate?: 'start' | 'end', ): Promise { return makeWorkerAndPost( { kind: 'ConvertTraceAndOpenInLegacy', trace, truncate, }, openTraceInLegacy, ); } export function convertTraceToPprofAndDownload( trace: Blob, pid: number, ts: time, ): Promise { return makeWorkerAndPost({ kind: 'ConvertTraceToPprof', trace, pid, ts, }); }