v8/tools/system-analyzer/lws-middleware.js
Camillo Bruni f2d079bc97 [tools][system-analyzer] Add local symbol server
Start a local symbol server using the local-web-sever node package:
   ws --stack system-analyzer/lws-middleware.js lws-static cors

The system-analyzer will then use it to symbolize profiles.

Note: The symbol server will execute `nm` and `objdump` locally.

Change-Id: Icff6e9f5af24f214f353c049f5cd13eedccf0f88
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2979591
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75501}
2021-07-01 09:59:17 +00:00

99 lines
2.8 KiB
JavaScript

// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const util = require('util');
const execFile = util.promisify(require('child_process').execFile);
const fs = require('fs')
async function sh(cmd, ...params) {
console.log(cmd, params.join(' '));
const options = {maxBuffer: 256 * 1024 * 1024};
const {stdout} = await execFile(cmd, params, options);
return stdout;
}
class Symbolizer {
constructor() {
this.nmExec = 'nm';
this.objdumpExec = 'objdump';
}
middleware(config) {
return async (ctx, next) => {
if (ctx.path == '/v8/loadVMSymbols') {
await this.parseVMSymbols(ctx)
}
await next()
}
}
async parseVMSymbols(ctx) {
const query = ctx.request.query;
const result = {
libName: query.libName,
symbols: ['', ''],
error: undefined,
fileOffsetMinusVma: 0,
};
switch (query.platform) {
case 'macos':
await this.loadVMSymbolsMacOS(query, result);
break;
case 'linux':
await this.loadVMSymbolsLinux(query, result);
break;
default:
ctx.response.status = '500';
return;
}
ctx.response.type = 'json';
ctx.response.body = JSON.stringify(result);
}
async loadVMSymbolsMacOS(query, result) {
let libName =
(query.targetRootFS ? query.targetRootFS : '') + query.libName;
try {
// Fast skip files that don't exist.
if (libName.indexOf('/') === -1 || !fs.existsSync(libName)) return;
result.symbols = [await sh(this.nmExec, '--demangle', '-n', libName), ''];
} catch (e) {
result.error = e.message;
}
}
async loadVMSymbolsLinux(query, result) {
let libName = query.libName;
if (query.apkEmbeddedLibrary && libName.endsWith('.apk')) {
libName = query.apkEmbeddedLibrary;
}
if (query.targetRootFS) {
libName = libName.substring(libName.lastIndexOf('/') + 1);
libName = query.targetRootFS + libName;
}
try {
// Fast skip files that don't exist.
if (libName.indexOf('/') === -1 || !fs.existsSync(libName)) return;
result.symbols = [
await sh(this.nmExec, '-C', '-n', '-S', libName),
await sh(this.nmExec, '-C', '-n', '-S', '-D', libName)
];
const objdumpOutput = await sh(this.objdumpExec, '-h', libName);
for (const line of objdumpOutput.split('\n')) {
const [, sectionName, , vma, , fileOffset] = line.trim().split(/\s+/);
if (sectionName === '.text') {
result.fileOffsetMinusVma =
parseInt(fileOffset, 16) - parseInt(vma, 16);
}
}
} catch (e) {
console.log(e);
result.error = e.message;
}
}
}
module.exports = Symbolizer