1// Copyright 2021 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5const util = require('util'); 6const execFile = util.promisify(require('child_process').execFile); 7const fs = require('fs') 8 9async function sh(cmd, ...params) { 10 console.log(cmd, params.join(' ')); 11 const options = {maxBuffer: 256 * 1024 * 1024}; 12 const {stdout} = await execFile(cmd, params, options); 13 return stdout; 14} 15 16class Symbolizer { 17 constructor() { 18 this.nmExec = 'nm'; 19 this.objdumpExec = 'objdump'; 20 } 21 22 middleware(config) { 23 return async (ctx, next) => { 24 if (ctx.path == '/v8/loadVMSymbols') { 25 await this.parseVMSymbols(ctx) 26 } 27 await next() 28 } 29 } 30 31 async parseVMSymbols(ctx) { 32 const query = ctx.request.query; 33 const result = { 34 libName: query.libName, 35 symbols: ['', ''], 36 error: undefined, 37 fileOffsetMinusVma: 0, 38 }; 39 switch (query.platform) { 40 case 'macos': 41 await this.loadVMSymbolsMacOS(query, result); 42 break; 43 case 'linux': 44 await this.loadVMSymbolsLinux(query, result); 45 break; 46 default: 47 ctx.response.status = '500'; 48 return; 49 } 50 ctx.response.type = 'json'; 51 ctx.response.body = JSON.stringify(result); 52 } 53 54 async loadVMSymbolsMacOS(query, result) { 55 let libName = 56 (query.targetRootFS ? query.targetRootFS : '') + query.libName; 57 try { 58 // Fast skip files that don't exist. 59 if (libName.indexOf('/') === -1 || !fs.existsSync(libName)) return; 60 result.symbols = [await sh(this.nmExec, '--demangle', '-n', libName), '']; 61 } catch (e) { 62 result.error = e.message; 63 } 64 } 65 66 async loadVMSymbolsLinux(query, result) { 67 let libName = query.libName; 68 if (query.apkEmbeddedLibrary && libName.endsWith('.apk')) { 69 libName = query.apkEmbeddedLibrary; 70 } 71 if (query.targetRootFS) { 72 libName = libName.substring(libName.lastIndexOf('/') + 1); 73 libName = query.targetRootFS + libName; 74 } 75 try { 76 // Fast skip files that don't exist. 77 if (libName.indexOf('/') === -1 || !fs.existsSync(libName)) return; 78 result.symbols = [ 79 await sh(this.nmExec, '-C', '-n', '-S', libName), 80 await sh(this.nmExec, '-C', '-n', '-S', '-D', libName) 81 ]; 82 83 const objdumpOutput = await sh(this.objdumpExec, '-h', libName); 84 for (const line of objdumpOutput.split('\n')) { 85 const [, sectionName, , vma, , fileOffset] = line.trim().split(/\s+/); 86 if (sectionName === '.text') { 87 result.fileOffsetMinusVma = 88 parseInt(fileOffset, 16) - parseInt(vma, 16); 89 } 90 } 91 } catch (e) { 92 console.log(e); 93 result.error = e.message; 94 } 95 } 96} 97 98module.exports = Symbolizer 99