1// Copyright (C) 2018 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 {defer} from '../base/deferred'; 16import {assertExists} from '../base/logging'; 17import {Actions} from '../common/actions'; 18import {TraceSource} from '../common/state'; 19import * as trace_to_text from '../gen/trace_to_text'; 20 21import {globals} from './globals'; 22 23type Format = 'json'|'systrace'; 24 25export function ConvertTrace( 26 trace: Blob, format: Format, truncate?: 'start'|'end') { 27 const outPath = '/trace.json'; 28 const args: string[] = [format]; 29 if (truncate !== undefined) { 30 args.push('--truncate', truncate); 31 } 32 args.push('/fs/trace.proto', outPath); 33 runTraceconv(trace, args).then(module => { 34 const fsNode = module.FS.lookupPath(outPath).node; 35 const data = fsNode.contents.buffer; 36 const size = fsNode.usedBytes; 37 globals.publish('LegacyTrace', {data, size}, /*transfer=*/[data]); 38 module.FS.unlink(outPath); 39 }); 40} 41 42export function ConvertTraceToPprof( 43 pid: number, src: TraceSource, ts1: number, ts2?: number) { 44 const timestamps = `${ts1}${ts2 === undefined ? '' : `,${ts2}`}`; 45 const args = [ 46 'profile', 47 `--pid`, 48 `${pid}`, 49 `--timestamps`, 50 timestamps, 51 '/fs/trace.proto' 52 ]; 53 generateBlob(src).then(traceBlob => { 54 runTraceconv(traceBlob, args).then(module => { 55 const heapDirName = 56 Object.keys(module.FS.lookupPath('/tmp/').node.contents)[0]; 57 const heapDirContents = 58 module.FS.lookupPath(`/tmp/${heapDirName}`).node.contents; 59 const heapDumpFiles = Object.keys(heapDirContents); 60 let fileNum = 0; 61 heapDumpFiles.forEach(heapDump => { 62 const fileContents = 63 module.FS.lookupPath(`/tmp/${heapDirName}/${heapDump}`) 64 .node.contents; 65 fileNum++; 66 const fileName = `/heap_dump.${fileNum}.${pid}.pb`; 67 downloadFile(new Blob([fileContents]), fileName); 68 }); 69 }); 70 }); 71} 72 73async function runTraceconv(trace: Blob, args: string[]) { 74 const deferredRuntimeInitialized = defer<void>(); 75 const module = trace_to_text({ 76 noInitialRun: true, 77 locateFile: (s: string) => s, 78 print: updateStatus, 79 printErr: updateStatus, 80 onRuntimeInitialized: () => deferredRuntimeInitialized.resolve() 81 }); 82 await deferredRuntimeInitialized; 83 module.FS.mkdir('/fs'); 84 module.FS.mount( 85 assertExists(module.FS.filesystems.WORKERFS), 86 {blobs: [{name: 'trace.proto', data: trace}]}, 87 '/fs'); 88 updateStatus('Converting trace'); 89 module.callMain(args); 90 updateStatus('Trace conversion completed'); 91 return module; 92} 93 94async function generateBlob(src: TraceSource) { 95 let blob: Blob = new Blob(); 96 if (src.type === 'URL') { 97 const resp = await fetch(src.url); 98 if (resp.status !== 200) { 99 throw new Error(`fetch() failed with HTTP error ${resp.status}`); 100 } 101 blob = await resp.blob(); 102 } else if (src.type === 'ARRAY_BUFFER') { 103 blob = new Blob([new Uint8Array(src.buffer, 0, src.buffer.byteLength)]); 104 } else if (src.type === 'FILE') { 105 blob = src.file; 106 } else { 107 throw new Error(`Conversion not supported for ${JSON.stringify(src)}`); 108 } 109 return blob; 110} 111 112function downloadFile(file: Blob, name: string) { 113 globals.publish('FileDownload', {file, name}); 114} 115 116function updateStatus(msg: {}) { 117 console.log(msg); 118 globals.dispatch(Actions.updateStatus({ 119 msg: msg.toString(), 120 timestamp: Date.now() / 1000, 121 })); 122} 123