1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2021 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Workerimport {defer} from '../base/deferred'; 16*6dbdd20aSAndroid Build Coastguard Workerimport { 17*6dbdd20aSAndroid Build Coastguard Worker addErrorHandler, 18*6dbdd20aSAndroid Build Coastguard Worker assertExists, 19*6dbdd20aSAndroid Build Coastguard Worker ErrorDetails, 20*6dbdd20aSAndroid Build Coastguard Worker reportError, 21*6dbdd20aSAndroid Build Coastguard Worker} from '../base/logging'; 22*6dbdd20aSAndroid Build Coastguard Workerimport {time} from '../base/time'; 23*6dbdd20aSAndroid Build Coastguard Workerimport traceconv from '../gen/traceconv'; 24*6dbdd20aSAndroid Build Coastguard Worker 25*6dbdd20aSAndroid Build Coastguard Workerconst selfWorker = self as {} as Worker; 26*6dbdd20aSAndroid Build Coastguard Worker 27*6dbdd20aSAndroid Build Coastguard Worker// TODO(hjd): The trace ends up being copied too many times due to how 28*6dbdd20aSAndroid Build Coastguard Worker// blob works. We should reduce the number of copies. 29*6dbdd20aSAndroid Build Coastguard Worker 30*6dbdd20aSAndroid Build Coastguard Workertype Format = 'json' | 'systrace'; 31*6dbdd20aSAndroid Build Coastguard Workertype Args = 32*6dbdd20aSAndroid Build Coastguard Worker | ConvertTraceAndDownloadArgs 33*6dbdd20aSAndroid Build Coastguard Worker | ConvertTraceAndOpenInLegacyArgs 34*6dbdd20aSAndroid Build Coastguard Worker | ConvertTraceToPprofArgs; 35*6dbdd20aSAndroid Build Coastguard Worker 36*6dbdd20aSAndroid Build Coastguard Workerfunction updateStatus(status: string) { 37*6dbdd20aSAndroid Build Coastguard Worker selfWorker.postMessage({ 38*6dbdd20aSAndroid Build Coastguard Worker kind: 'updateStatus', 39*6dbdd20aSAndroid Build Coastguard Worker status, 40*6dbdd20aSAndroid Build Coastguard Worker }); 41*6dbdd20aSAndroid Build Coastguard Worker} 42*6dbdd20aSAndroid Build Coastguard Worker 43*6dbdd20aSAndroid Build Coastguard Workerfunction notifyJobCompleted() { 44*6dbdd20aSAndroid Build Coastguard Worker selfWorker.postMessage({kind: 'jobCompleted'}); 45*6dbdd20aSAndroid Build Coastguard Worker} 46*6dbdd20aSAndroid Build Coastguard Worker 47*6dbdd20aSAndroid Build Coastguard Workerfunction downloadFile(buffer: Uint8Array, name: string) { 48*6dbdd20aSAndroid Build Coastguard Worker selfWorker.postMessage( 49*6dbdd20aSAndroid Build Coastguard Worker { 50*6dbdd20aSAndroid Build Coastguard Worker kind: 'downloadFile', 51*6dbdd20aSAndroid Build Coastguard Worker buffer, 52*6dbdd20aSAndroid Build Coastguard Worker name, 53*6dbdd20aSAndroid Build Coastguard Worker }, 54*6dbdd20aSAndroid Build Coastguard Worker [buffer.buffer], 55*6dbdd20aSAndroid Build Coastguard Worker ); 56*6dbdd20aSAndroid Build Coastguard Worker} 57*6dbdd20aSAndroid Build Coastguard Worker 58*6dbdd20aSAndroid Build Coastguard Workerfunction openTraceInLegacy(buffer: Uint8Array) { 59*6dbdd20aSAndroid Build Coastguard Worker selfWorker.postMessage({ 60*6dbdd20aSAndroid Build Coastguard Worker kind: 'openTraceInLegacy', 61*6dbdd20aSAndroid Build Coastguard Worker buffer, 62*6dbdd20aSAndroid Build Coastguard Worker }); 63*6dbdd20aSAndroid Build Coastguard Worker} 64*6dbdd20aSAndroid Build Coastguard Worker 65*6dbdd20aSAndroid Build Coastguard Workerfunction forwardError(error: ErrorDetails) { 66*6dbdd20aSAndroid Build Coastguard Worker selfWorker.postMessage({ 67*6dbdd20aSAndroid Build Coastguard Worker kind: 'error', 68*6dbdd20aSAndroid Build Coastguard Worker error, 69*6dbdd20aSAndroid Build Coastguard Worker }); 70*6dbdd20aSAndroid Build Coastguard Worker} 71*6dbdd20aSAndroid Build Coastguard Worker 72*6dbdd20aSAndroid Build Coastguard Workerfunction fsNodeToBuffer(fsNode: traceconv.FileSystemNode): Uint8Array { 73*6dbdd20aSAndroid Build Coastguard Worker const fileSize = assertExists(fsNode.usedBytes); 74*6dbdd20aSAndroid Build Coastguard Worker return new Uint8Array(fsNode.contents.buffer, 0, fileSize); 75*6dbdd20aSAndroid Build Coastguard Worker} 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Workerasync function runTraceconv(trace: Blob, args: string[]) { 78*6dbdd20aSAndroid Build Coastguard Worker const deferredRuntimeInitialized = defer<void>(); 79*6dbdd20aSAndroid Build Coastguard Worker const module = traceconv({ 80*6dbdd20aSAndroid Build Coastguard Worker noInitialRun: true, 81*6dbdd20aSAndroid Build Coastguard Worker locateFile: (s: string) => s, 82*6dbdd20aSAndroid Build Coastguard Worker print: updateStatus, 83*6dbdd20aSAndroid Build Coastguard Worker printErr: updateStatus, 84*6dbdd20aSAndroid Build Coastguard Worker onRuntimeInitialized: () => deferredRuntimeInitialized.resolve(), 85*6dbdd20aSAndroid Build Coastguard Worker }); 86*6dbdd20aSAndroid Build Coastguard Worker await deferredRuntimeInitialized; 87*6dbdd20aSAndroid Build Coastguard Worker module.FS.mkdir('/fs'); 88*6dbdd20aSAndroid Build Coastguard Worker module.FS.mount( 89*6dbdd20aSAndroid Build Coastguard Worker assertExists(module.FS.filesystems.WORKERFS), 90*6dbdd20aSAndroid Build Coastguard Worker {blobs: [{name: 'trace.proto', data: trace}]}, 91*6dbdd20aSAndroid Build Coastguard Worker '/fs', 92*6dbdd20aSAndroid Build Coastguard Worker ); 93*6dbdd20aSAndroid Build Coastguard Worker updateStatus('Converting trace'); 94*6dbdd20aSAndroid Build Coastguard Worker module.callMain(args); 95*6dbdd20aSAndroid Build Coastguard Worker updateStatus('Trace conversion completed'); 96*6dbdd20aSAndroid Build Coastguard Worker return module; 97*6dbdd20aSAndroid Build Coastguard Worker} 98*6dbdd20aSAndroid Build Coastguard Worker 99*6dbdd20aSAndroid Build Coastguard Workerinterface ConvertTraceAndDownloadArgs { 100*6dbdd20aSAndroid Build Coastguard Worker kind: 'ConvertTraceAndDownload'; 101*6dbdd20aSAndroid Build Coastguard Worker trace: Blob; 102*6dbdd20aSAndroid Build Coastguard Worker format: Format; 103*6dbdd20aSAndroid Build Coastguard Worker truncate?: 'start' | 'end'; 104*6dbdd20aSAndroid Build Coastguard Worker} 105*6dbdd20aSAndroid Build Coastguard Worker 106*6dbdd20aSAndroid Build Coastguard Workerfunction isConvertTraceAndDownload( 107*6dbdd20aSAndroid Build Coastguard Worker msg: Args, 108*6dbdd20aSAndroid Build Coastguard Worker): msg is ConvertTraceAndDownloadArgs { 109*6dbdd20aSAndroid Build Coastguard Worker if (msg.kind !== 'ConvertTraceAndDownload') { 110*6dbdd20aSAndroid Build Coastguard Worker return false; 111*6dbdd20aSAndroid Build Coastguard Worker } 112*6dbdd20aSAndroid Build Coastguard Worker if (msg.trace === undefined) { 113*6dbdd20aSAndroid Build Coastguard Worker throw new Error('ConvertTraceAndDownloadArgs missing trace'); 114*6dbdd20aSAndroid Build Coastguard Worker } 115*6dbdd20aSAndroid Build Coastguard Worker if (msg.format !== 'json' && msg.format !== 'systrace') { 116*6dbdd20aSAndroid Build Coastguard Worker throw new Error('ConvertTraceAndDownloadArgs has bad format'); 117*6dbdd20aSAndroid Build Coastguard Worker } 118*6dbdd20aSAndroid Build Coastguard Worker return true; 119*6dbdd20aSAndroid Build Coastguard Worker} 120*6dbdd20aSAndroid Build Coastguard Worker 121*6dbdd20aSAndroid Build Coastguard Workerasync function ConvertTraceAndDownload( 122*6dbdd20aSAndroid Build Coastguard Worker trace: Blob, 123*6dbdd20aSAndroid Build Coastguard Worker format: Format, 124*6dbdd20aSAndroid Build Coastguard Worker truncate?: 'start' | 'end', 125*6dbdd20aSAndroid Build Coastguard Worker): Promise<void> { 126*6dbdd20aSAndroid Build Coastguard Worker const outPath = '/trace.json'; 127*6dbdd20aSAndroid Build Coastguard Worker const args: string[] = [format]; 128*6dbdd20aSAndroid Build Coastguard Worker if (truncate !== undefined) { 129*6dbdd20aSAndroid Build Coastguard Worker args.push('--truncate', truncate); 130*6dbdd20aSAndroid Build Coastguard Worker } 131*6dbdd20aSAndroid Build Coastguard Worker args.push('/fs/trace.proto', outPath); 132*6dbdd20aSAndroid Build Coastguard Worker try { 133*6dbdd20aSAndroid Build Coastguard Worker const module = await runTraceconv(trace, args); 134*6dbdd20aSAndroid Build Coastguard Worker const fsNode = module.FS.lookupPath(outPath).node; 135*6dbdd20aSAndroid Build Coastguard Worker downloadFile(fsNodeToBuffer(fsNode), `trace.${format}`); 136*6dbdd20aSAndroid Build Coastguard Worker module.FS.unlink(outPath); 137*6dbdd20aSAndroid Build Coastguard Worker } finally { 138*6dbdd20aSAndroid Build Coastguard Worker notifyJobCompleted(); 139*6dbdd20aSAndroid Build Coastguard Worker } 140*6dbdd20aSAndroid Build Coastguard Worker} 141*6dbdd20aSAndroid Build Coastguard Worker 142*6dbdd20aSAndroid Build Coastguard Workerinterface ConvertTraceAndOpenInLegacyArgs { 143*6dbdd20aSAndroid Build Coastguard Worker kind: 'ConvertTraceAndOpenInLegacy'; 144*6dbdd20aSAndroid Build Coastguard Worker trace: Blob; 145*6dbdd20aSAndroid Build Coastguard Worker truncate?: 'start' | 'end'; 146*6dbdd20aSAndroid Build Coastguard Worker} 147*6dbdd20aSAndroid Build Coastguard Worker 148*6dbdd20aSAndroid Build Coastguard Workerfunction isConvertTraceAndOpenInLegacy( 149*6dbdd20aSAndroid Build Coastguard Worker msg: Args, 150*6dbdd20aSAndroid Build Coastguard Worker): msg is ConvertTraceAndOpenInLegacyArgs { 151*6dbdd20aSAndroid Build Coastguard Worker if (msg.kind !== 'ConvertTraceAndOpenInLegacy') { 152*6dbdd20aSAndroid Build Coastguard Worker return false; 153*6dbdd20aSAndroid Build Coastguard Worker } 154*6dbdd20aSAndroid Build Coastguard Worker return true; 155*6dbdd20aSAndroid Build Coastguard Worker} 156*6dbdd20aSAndroid Build Coastguard Worker 157*6dbdd20aSAndroid Build Coastguard Workerasync function ConvertTraceAndOpenInLegacy( 158*6dbdd20aSAndroid Build Coastguard Worker trace: Blob, 159*6dbdd20aSAndroid Build Coastguard Worker truncate?: 'start' | 'end', 160*6dbdd20aSAndroid Build Coastguard Worker) { 161*6dbdd20aSAndroid Build Coastguard Worker const outPath = '/trace.json'; 162*6dbdd20aSAndroid Build Coastguard Worker const args: string[] = ['json']; 163*6dbdd20aSAndroid Build Coastguard Worker if (truncate !== undefined) { 164*6dbdd20aSAndroid Build Coastguard Worker args.push('--truncate', truncate); 165*6dbdd20aSAndroid Build Coastguard Worker } 166*6dbdd20aSAndroid Build Coastguard Worker args.push('/fs/trace.proto', outPath); 167*6dbdd20aSAndroid Build Coastguard Worker try { 168*6dbdd20aSAndroid Build Coastguard Worker const module = await runTraceconv(trace, args); 169*6dbdd20aSAndroid Build Coastguard Worker const fsNode = module.FS.lookupPath(outPath).node; 170*6dbdd20aSAndroid Build Coastguard Worker const data = fsNode.contents.buffer; 171*6dbdd20aSAndroid Build Coastguard Worker const size = fsNode.usedBytes; 172*6dbdd20aSAndroid Build Coastguard Worker const buffer = new Uint8Array(data, 0, size); 173*6dbdd20aSAndroid Build Coastguard Worker openTraceInLegacy(buffer); 174*6dbdd20aSAndroid Build Coastguard Worker module.FS.unlink(outPath); 175*6dbdd20aSAndroid Build Coastguard Worker } finally { 176*6dbdd20aSAndroid Build Coastguard Worker notifyJobCompleted(); 177*6dbdd20aSAndroid Build Coastguard Worker } 178*6dbdd20aSAndroid Build Coastguard Worker} 179*6dbdd20aSAndroid Build Coastguard Worker 180*6dbdd20aSAndroid Build Coastguard Workerinterface ConvertTraceToPprofArgs { 181*6dbdd20aSAndroid Build Coastguard Worker kind: 'ConvertTraceToPprof'; 182*6dbdd20aSAndroid Build Coastguard Worker trace: Blob; 183*6dbdd20aSAndroid Build Coastguard Worker pid: number; 184*6dbdd20aSAndroid Build Coastguard Worker ts: time; 185*6dbdd20aSAndroid Build Coastguard Worker} 186*6dbdd20aSAndroid Build Coastguard Worker 187*6dbdd20aSAndroid Build Coastguard Workerfunction isConvertTraceToPprof(msg: Args): msg is ConvertTraceToPprofArgs { 188*6dbdd20aSAndroid Build Coastguard Worker if (msg.kind !== 'ConvertTraceToPprof') { 189*6dbdd20aSAndroid Build Coastguard Worker return false; 190*6dbdd20aSAndroid Build Coastguard Worker } 191*6dbdd20aSAndroid Build Coastguard Worker return true; 192*6dbdd20aSAndroid Build Coastguard Worker} 193*6dbdd20aSAndroid Build Coastguard Worker 194*6dbdd20aSAndroid Build Coastguard Workerasync function ConvertTraceToPprof(trace: Blob, pid: number, ts: time) { 195*6dbdd20aSAndroid Build Coastguard Worker const args = [ 196*6dbdd20aSAndroid Build Coastguard Worker 'profile', 197*6dbdd20aSAndroid Build Coastguard Worker `--pid`, 198*6dbdd20aSAndroid Build Coastguard Worker `${pid}`, 199*6dbdd20aSAndroid Build Coastguard Worker `--timestamps`, 200*6dbdd20aSAndroid Build Coastguard Worker `${ts}`, 201*6dbdd20aSAndroid Build Coastguard Worker '/fs/trace.proto', 202*6dbdd20aSAndroid Build Coastguard Worker ]; 203*6dbdd20aSAndroid Build Coastguard Worker 204*6dbdd20aSAndroid Build Coastguard Worker try { 205*6dbdd20aSAndroid Build Coastguard Worker const module = await runTraceconv(trace, args); 206*6dbdd20aSAndroid Build Coastguard Worker const heapDirName = Object.keys( 207*6dbdd20aSAndroid Build Coastguard Worker module.FS.lookupPath('/tmp/').node.contents, 208*6dbdd20aSAndroid Build Coastguard Worker )[0]; 209*6dbdd20aSAndroid Build Coastguard Worker const heapDirContents = module.FS.lookupPath(`/tmp/${heapDirName}`).node 210*6dbdd20aSAndroid Build Coastguard Worker .contents; 211*6dbdd20aSAndroid Build Coastguard Worker const heapDumpFiles = Object.keys(heapDirContents); 212*6dbdd20aSAndroid Build Coastguard Worker for (let i = 0; i < heapDumpFiles.length; ++i) { 213*6dbdd20aSAndroid Build Coastguard Worker const heapDump = heapDumpFiles[i]; 214*6dbdd20aSAndroid Build Coastguard Worker const fileNode = module.FS.lookupPath( 215*6dbdd20aSAndroid Build Coastguard Worker `/tmp/${heapDirName}/${heapDump}`, 216*6dbdd20aSAndroid Build Coastguard Worker ).node; 217*6dbdd20aSAndroid Build Coastguard Worker const fileName = `/heap_dump.${i}.${pid}.pb`; 218*6dbdd20aSAndroid Build Coastguard Worker downloadFile(fsNodeToBuffer(fileNode), fileName); 219*6dbdd20aSAndroid Build Coastguard Worker } 220*6dbdd20aSAndroid Build Coastguard Worker } finally { 221*6dbdd20aSAndroid Build Coastguard Worker notifyJobCompleted(); 222*6dbdd20aSAndroid Build Coastguard Worker } 223*6dbdd20aSAndroid Build Coastguard Worker} 224*6dbdd20aSAndroid Build Coastguard Worker 225*6dbdd20aSAndroid Build Coastguard WorkerselfWorker.onmessage = (msg: MessageEvent) => { 226*6dbdd20aSAndroid Build Coastguard Worker self.addEventListener('error', (e) => reportError(e)); 227*6dbdd20aSAndroid Build Coastguard Worker self.addEventListener('unhandledrejection', (e) => reportError(e)); 228*6dbdd20aSAndroid Build Coastguard Worker addErrorHandler((error: ErrorDetails) => forwardError(error)); 229*6dbdd20aSAndroid Build Coastguard Worker const args = msg.data as Args; 230*6dbdd20aSAndroid Build Coastguard Worker if (isConvertTraceAndDownload(args)) { 231*6dbdd20aSAndroid Build Coastguard Worker ConvertTraceAndDownload(args.trace, args.format, args.truncate); 232*6dbdd20aSAndroid Build Coastguard Worker } else if (isConvertTraceAndOpenInLegacy(args)) { 233*6dbdd20aSAndroid Build Coastguard Worker ConvertTraceAndOpenInLegacy(args.trace, args.truncate); 234*6dbdd20aSAndroid Build Coastguard Worker } else if (isConvertTraceToPprof(args)) { 235*6dbdd20aSAndroid Build Coastguard Worker ConvertTraceToPprof(args.trace, args.pid, args.ts); 236*6dbdd20aSAndroid Build Coastguard Worker } else { 237*6dbdd20aSAndroid Build Coastguard Worker throw new Error(`Unknown method call ${JSON.stringify(args)}`); 238*6dbdd20aSAndroid Build Coastguard Worker } 239*6dbdd20aSAndroid Build Coastguard Worker}; 240