1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {assetSrc} from '../base/assets'; 16import {download} from '../base/clipboard'; 17import {defer} from '../base/deferred'; 18import {ErrorDetails} from '../base/logging'; 19import {utf8Decode} from '../base/string_utils'; 20import {time} from '../base/time'; 21import {AppImpl} from '../core/app_impl'; 22import {maybeShowErrorDialog} from './error_dialog'; 23 24type Args = 25 | UpdateStatusArgs 26 | JobCompletedArgs 27 | DownloadFileArgs 28 | OpenTraceInLegacyArgs 29 | ErrorArgs; 30 31interface UpdateStatusArgs { 32 kind: 'updateStatus'; 33 status: string; 34} 35 36interface JobCompletedArgs { 37 kind: 'jobCompleted'; 38} 39 40interface DownloadFileArgs { 41 kind: 'downloadFile'; 42 buffer: Uint8Array; 43 name: string; 44} 45 46interface OpenTraceInLegacyArgs { 47 kind: 'openTraceInLegacy'; 48 buffer: Uint8Array; 49} 50 51interface ErrorArgs { 52 kind: 'error'; 53 error: ErrorDetails; 54} 55 56type OpenTraceInLegacyCallback = ( 57 name: string, 58 data: ArrayBuffer | string, 59 size: number, 60) => void; 61 62async function makeWorkerAndPost( 63 msg: unknown, 64 openTraceInLegacy?: OpenTraceInLegacyCallback, 65) { 66 const promise = defer<void>(); 67 68 function handleOnMessage(msg: MessageEvent): void { 69 const args: Args = msg.data; 70 if (args.kind === 'updateStatus') { 71 AppImpl.instance.omnibox.showStatusMessage(args.status); 72 } else if (args.kind === 'jobCompleted') { 73 promise.resolve(); 74 } else if (args.kind === 'downloadFile') { 75 download(new File([new Blob([args.buffer])], args.name)); 76 } else if (args.kind === 'openTraceInLegacy') { 77 const str = utf8Decode(args.buffer); 78 openTraceInLegacy?.('trace.json', str, 0); 79 } else if (args.kind === 'error') { 80 maybeShowErrorDialog(args.error); 81 } else { 82 throw new Error(`Unhandled message ${JSON.stringify(args)}`); 83 } 84 } 85 86 const worker = new Worker(assetSrc('traceconv_bundle.js')); 87 worker.onmessage = handleOnMessage; 88 worker.postMessage(msg); 89 return promise; 90} 91 92export function convertTraceToJsonAndDownload(trace: Blob): Promise<void> { 93 return makeWorkerAndPost({ 94 kind: 'ConvertTraceAndDownload', 95 trace, 96 format: 'json', 97 }); 98} 99 100export function convertTraceToSystraceAndDownload(trace: Blob): Promise<void> { 101 return makeWorkerAndPost({ 102 kind: 'ConvertTraceAndDownload', 103 trace, 104 format: 'systrace', 105 }); 106} 107 108export function convertToJson( 109 trace: Blob, 110 openTraceInLegacy: OpenTraceInLegacyCallback, 111 truncate?: 'start' | 'end', 112): Promise<void> { 113 return makeWorkerAndPost( 114 { 115 kind: 'ConvertTraceAndOpenInLegacy', 116 trace, 117 truncate, 118 }, 119 openTraceInLegacy, 120 ); 121} 122 123export function convertTraceToPprofAndDownload( 124 trace: Blob, 125 pid: number, 126 ts: time, 127): Promise<void> { 128 return makeWorkerAndPost({ 129 kind: 'ConvertTraceToPprof', 130 trace, 131 pid, 132 ts, 133 }); 134} 135