• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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