Commit f2d079bc authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[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: 's avatarVictor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75501}
parent 56fe020e
......@@ -6,7 +6,7 @@ import {
CppProcessor, LinuxCppEntriesProvider
} from "../../../tools/dumpcpp.mjs" ;
(function testProcessSharedLibrary() {
await (async function testProcessSharedLibrary() {
var oldLoadSymbols = LinuxCppEntriesProvider.prototype.loadSymbols;
LinuxCppEntriesProvider.prototype.loadSymbols = function(libName) {
......@@ -20,7 +20,7 @@ import {
var testCppProcessor = new CppProcessor(new LinuxCppEntriesProvider(),
false, false);
testCppProcessor.processSharedLibrary(
await testCppProcessor.processSharedLibrary(
'/usr/local/google/home/lpy/v8/out/native/d8',
0x00000100, 0x00000400, 0);
......
......@@ -34,8 +34,8 @@ const result = doWork();
const logString = d8.log.getAndStop();
const processor = new Processor();
processor.processChunk(logString);
processor.finalize();
await processor.processChunk(logString);
await processor.finalize();
const maps = processor.mapTimeline;
const ics = processor.icTimeline;
......
......@@ -82,7 +82,7 @@ import {
})();
(function testUnixCppEntriesProvider() {
await (async function testUnixCppEntriesProvider() {
var oldLoadSymbols = LinuxCppEntriesProvider.prototype.loadSymbols;
// shell executable
......@@ -99,13 +99,10 @@ import {
'081f08a0 00000004 B stdout\n'
].join('\n'), ''];
};
var shell_prov = new LinuxCppEntriesProvider();
var shell_syms = [];
shell_prov.parseVmSymbols('shell', 0x08048000, 0x081ee000, 0,
function (name, start, end) {
shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await shell_prov.parseVmSymbols('shell', 0x08048000, 0x081ee000, 0,
(...params) => shell_syms.push(params));
assertEquals(
[['_init', 0x08049790, 0x08049f50],
['_start', 0x08049f50, 0x08139150],
......@@ -128,10 +125,8 @@ import {
};
var libc_prov = new LinuxCppEntriesProvider();
var libc_syms = [];
libc_prov.parseVmSymbols('libc', 0xf7c5c000, 0xf7da5000, 0,
function (name, start, end) {
libc_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await libc_prov.parseVmSymbols('libc', 0xf7c5c000, 0xf7da5000, 0,
(...params) => libc_syms.push(params));
var libc_ref_syms = [['__libc_init_first', 0x000162a0, 0x000162a0 + 0x5],
['__isnan', 0x0002a5f0, 0x0002a5f0 + 0x2d],
['scalblnf', 0x0002aaa0, 0x0002aaa0 + 0xd],
......@@ -165,10 +160,8 @@ import {
};
var android_prov = new LinuxCppEntriesProvider();
var android_syms = [];
android_prov.parseVmSymbols('libmonochrome', 0xf7c5c000, 0xf9c5c000, 0,
function (name, start, end) {
android_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await android_prov.parseVmSymbols('libmonochrome', 0xf7c5c000, 0xf9c5c000, 0,
(...params) => android_syms.push(params));
var android_ref_syms = [
['v8::internal::interpreter::BytecodeGenerator::BytecodeGenerator(v8::internal::UnoptimizedCompilationInfo*)', 0x013a1088, 0x013a1088 + 0x224],
['v8::internal::interpreter::BytecodeGenerator::FinalizeBytecode(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Script>)', 0x013a12ac, 0x013a12ac + 0xd0],
......@@ -187,7 +180,7 @@ import {
})();
(function testMacOSCppEntriesProvider() {
await (async function testMacOSCppEntriesProvider() {
var oldLoadSymbols = MacOSCppEntriesProvider.prototype.loadSymbols;
// shell executable
......@@ -203,13 +196,10 @@ import {
'00137400 v8::internal::Runtime_DebugGetPropertyDetails\n'
].join('\n'), ''];
};
var shell_prov = new MacOSCppEntriesProvider();
var shell_syms = [];
shell_prov.parseVmSymbols('shell', 0x00001c00, 0x00163256, 0x100,
function (name, start, end) {
shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await shell_prov.parseVmSymbols('shell', 0x00001c00, 0x00163256, 0x100,
(...params) => shell_syms.push(params));
assertEquals(
[['start', 0x00001c00, 0x00001c40],
['dyld_stub_binding_helper', 0x00001c40, 0x0011b810],
......@@ -229,10 +219,8 @@ import {
};
var stdc_prov = new MacOSCppEntriesProvider();
var stdc_syms = [];
stdc_prov.parseVmSymbols('stdc++', 0x95728fb4, 0x95770005, 0,
function (name, start, end) {
stdc_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await stdc_prov.parseVmSymbols('stdc++', 0x95728fb4, 0x95770005, 0,
(...params) => stdc_syms.push(params));
var stdc_ref_syms = [['__gnu_cxx::balloc::__mini_vector<std::pair<__gnu_cxx::bitmap_allocator<char>::_Alloc_block*, __gnu_cxx::bitmap_allocator<char>::_Alloc_block*> >::__mini_vector', 0x0000107a, 0x0002c410],
['std::basic_streambuf<char, std::char_traits<char> >::pubseekoff', 0x0002c410, 0x0002c488],
['std::basic_streambuf<char, std::char_traits<char> >::pubseekpos', 0x0002c488, 0x000466aa],
......@@ -247,7 +235,7 @@ import {
})();
(function testWindowsCppEntriesProvider() {
await (async function testWindowsCppEntriesProvider() {
var oldLoadSymbols = WindowsCppEntriesProvider.prototype.loadSymbols;
WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
......@@ -272,10 +260,8 @@ import {
};
var shell_prov = new WindowsCppEntriesProvider();
var shell_syms = [];
shell_prov.parseVmSymbols('shell.exe', 0x00400000, 0x0057c000, 0,
function (name, start, end) {
shell_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
await shell_prov.parseVmSymbols('shell.exe', 0x00400000, 0x0057c000, 0,
(...params) => shell_syms.push(params));
assertEquals(
[['ReadFile', 0x00401000, 0x004010a0],
['Print', 0x004010a0, 0x00402230],
......@@ -289,7 +275,7 @@ import {
// http://code.google.com/p/v8/issues/detail?id=427
(function testWindowsProcessExeAndDllMapFile() {
await (async function testWindowsProcessExeAndDllMapFile() {
function exeSymbols(exeName) {
return [
' 0000:00000000 ___ImageBase 00400000 <linker-defined>',
......@@ -312,11 +298,9 @@ import {
read = exeSymbols;
var exe_exe_syms = [];
(new WindowsCppEntriesProvider()).parseVmSymbols(
await (new WindowsCppEntriesProvider()).parseVmSymbols(
'chrome.exe', 0x00400000, 0x00472000, 0,
function (name, start, end) {
exe_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
(...params) => exe_exe_syms.push(params));
assertEquals(
[['RunMain', 0x00401780, 0x00401ac0],
['_main', 0x00401ac0, 0x00472000]],
......@@ -324,22 +308,18 @@ import {
read = dllSymbols;
var exe_dll_syms = [];
(new WindowsCppEntriesProvider()).parseVmSymbols(
await (new WindowsCppEntriesProvider()).parseVmSymbols(
'chrome.exe', 0x00400000, 0x00472000, 0,
function (name, start, end) {
exe_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
(...params) => exe_dll_syms.push(params));
assertEquals(
[],
exe_dll_syms, '.exe with .dll symbols');
read = dllSymbols;
var dll_dll_syms = [];
(new WindowsCppEntriesProvider()).parseVmSymbols(
await (new WindowsCppEntriesProvider()).parseVmSymbols(
'chrome.dll', 0x01c30000, 0x02b80000, 0,
function (name, start, end) {
dll_dll_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
(...params) => dll_dll_syms.push(params));
assertEquals(
[['_DllMain@12', 0x01c31780, 0x01c31ac0],
['___DllMainCRTStartup', 0x01c31ac0, 0x02b80000]],
......@@ -347,11 +327,9 @@ import {
read = exeSymbols;
var dll_exe_syms = [];
(new WindowsCppEntriesProvider()).parseVmSymbols(
await (new WindowsCppEntriesProvider()).parseVmSymbols(
'chrome.dll', 0x01c30000, 0x02b80000, 0,
function (name, start, end) {
dll_exe_syms.push(Array.prototype.slice.apply(arguments, [0]));
});
(...params) => dll_exe_syms.push(params));
assertEquals(
[],
dll_exe_syms, '.dll with .exe symbols');
......@@ -431,7 +409,7 @@ class PrintMonitor {
}
}
(function testProcessing() {
await (async function testProcessing() {
const testData = {
'Default': [
'tickprocessor-test.log', 'tickprocessor-test.default',
......@@ -462,30 +440,30 @@ class PrintMonitor {
};
for (var testName in testData) {
console.log('=== testProcessing-' + testName + ' ===');
testTickProcessor(...testData[testName]);
await testTickProcessor(...testData[testName]);
}
})();
function testTickProcessor(logInput, refOutput, args=[]) {
async function testTickProcessor(logInput, refOutput, args=[]) {
// /foo/bar/tickprocesser.mjs => /foo/bar/
const dir = import.meta.url.split("/").slice(0, -1).join('/') + '/';
const params = ArgumentsProcessor.process(args);
testExpectations(dir, logInput, refOutput, params);
await testExpectations(dir, logInput, refOutput, params);
// TODO(cbruni): enable again after it works on bots
// testEndToEnd(dir, 'tickprocessor-test-large.js', refOutput, params);
// await testEndToEnd(dir, 'tickprocessor-test-large.js', refOutput, params);
}
function testExpectations(dir, logInput, refOutput, params) {
async function testExpectations(dir, logInput, refOutput, params) {
const symbolsFile = dir + logInput + '.symbols.json';
const cppEntries = new CppEntriesProviderMock(symbolsFile);
const tickProcessor = TickProcessor.fromParams(params, cppEntries);
const printMonitor = new PrintMonitor(dir + refOutput);
tickProcessor.processLogFileInTest(dir + logInput);
await tickProcessor.processLogFileInTest(dir + logInput);
tickProcessor.printStatistics();
printMonitor.finish();
};
function testEndToEnd(dir, sourceFile, ignoredRefOutput, params) {
async function testEndToEnd(dir, sourceFile, ignoredRefOutput, params) {
// This test only works on linux.
if (!os?.system) return;
if (os.name !== 'linux' && os.name !== 'macos') return;
......@@ -499,6 +477,6 @@ function testEndToEnd(dir, sourceFile, ignoredRefOutput, params) {
// hence we cannot properly compare output expectations.
// Let's just use a dummy file and only test whether we don't throw.
const printMonitor = new PrintMonitor(dir + ignoredRefOutput);
tickProcessor.processLogFileInTest(tmpLogFile);
await tickProcessor.processLogFileInTest(tmpLogFile);
tickProcessor.printStatistics();
}
\ No newline at end of file
......@@ -20,5 +20,5 @@ const cppProcessor = new CppProcessor(
new (entriesProviders[params.platform])(params.nm, params.targetRootFS,
params.apkEmbeddedLibrary),
params.timedRange, params.pairwiseTimedRange);
cppProcessor.processLogFile(params.logFileName);
await cppProcessor.processLogFile(params.logFileName);
cppProcessor.dumpCppSymbols();
......@@ -22,7 +22,7 @@ class ArgumentsProcessor extends BaseArgumentsProcessor {
const params = ArgumentsProcessor.process(arguments);
const processor = new Processor();
processor.processLogFile(params.logFileName);
await processor.processLogFile(params.logFileName);
const typeAccumulator = new Map();
......
......@@ -117,8 +117,8 @@ export class LogReader {
*
* @param {string} chunk A portion of log.
*/
processLogChunk(chunk) {
this.processLog_(chunk.split('\n'));
async processLogChunk(chunk) {
await this.processLog_(chunk.split('\n'));
}
/**
......@@ -126,14 +126,14 @@ export class LogReader {
*
* @param {string} line A line of log.
*/
processLogLine(line) {
async processLogLine(line) {
if (!this.timedRange_) {
this.processLogLine_(line);
await this.processLogLine_(line);
return;
}
if (line.startsWith("current-time")) {
if (this.hasSeenTimerMarker_) {
this.processLog_(this.logLinesSinceLastTimerMarker_);
await this.processLog_(this.logLinesSinceLastTimerMarker_);
this.logLinesSinceLastTimerMarker_ = [];
// In pairwise mode, a "current-time" line ends the timed range.
if (this.pairwiseTimedRange_) {
......@@ -146,7 +146,7 @@ export class LogReader {
if (this.hasSeenTimerMarker_) {
this.logLinesSinceLastTimerMarker_.push(line);
} else if (!line.startsWith("tick")) {
this.processLogLine_(line);
await this.processLogLine_(line);
}
}
}
......@@ -195,7 +195,7 @@ export class LogReader {
* @param {Array.<string>} fields Log record.
* @private
*/
dispatchLogRow_(fields) {
async dispatchLogRow_(fields) {
// Obtain the dispatch.
const command = fields[0];
const dispatch = this.dispatchTable_[command];
......@@ -222,7 +222,7 @@ export class LogReader {
}
// Run the processor.
dispatch.processor.apply(this, parsedFields);
await dispatch.processor.apply(this, parsedFields);
}
/**
......@@ -231,9 +231,9 @@ export class LogReader {
* @param {Array.<string>} lines Log lines.
* @private
*/
processLog_(lines) {
async processLog_(lines) {
for (let i = 0, n = lines.length; i < n; ++i) {
this.processLogLine_(lines[i]);
await this.processLogLine_(lines[i]);
}
}
......@@ -243,11 +243,11 @@ export class LogReader {
* @param {String} a log line
* @private
*/
processLogLine_(line) {
async processLogLine_(line) {
if (line.length > 0) {
try {
const fields = this.csvParser_.parseLine(line);
this.dispatchLogRow_(fields);
await this.dispatchLogRow_(fields);
} catch (e) {
this.printError(`line ${this.lineNum_ + 1}: ${e.message || e}\n${e.stack}`);
}
......
......@@ -6,4 +6,4 @@ import { ParseProcessor, ArgumentsProcessor } from "./parse-processor.mjs";
const params = ArgumentsProcessor.process(arguments);
const parseProcessor = new ParseProcessor();
parseProcessor.processLogFile(params.logFileName);
await parseProcessor.processLogFile(params.logFileName);
......@@ -56,7 +56,7 @@ class App {
'fileuploadchunk', (e) => this.handleFileUploadChunk(e));
this._view.logFileReader.addEventListener(
'fileuploadend', (e) => this.handleFileUploadEnd(e));
this._startupPromise = this.runAsyncInitialize();
this._startupPromise = this._loadCustomElements();
this._view.codeTrack.svg = true;
}
......@@ -74,7 +74,7 @@ class App {
]);
}
async runAsyncInitialize() {
async _loadCustomElements() {
await Promise.all([
import('./view/list-panel.mjs'),
import('./view/timeline-panel.mjs'),
......@@ -84,6 +84,10 @@ class App {
import('./view/property-link-table.mjs'),
import('./view/tool-tip.mjs'),
]);
this._addEventListeners();
}
_addEventListeners() {
document.addEventListener(
'keydown', e => this._navigation?.handleKeyDown(e));
document.addEventListener(
......@@ -359,15 +363,14 @@ class App {
this._processor = new Processor();
}
handleFileUploadChunk(e) {
async handleFileUploadChunk(e) {
this._processor.processChunk(e.detail);
}
async handleFileUploadEnd(e) {
try {
const processor = this._processor;
processor.finalize();
await processor.finalize();
await this._startupPromise;
this._state.profile = processor.profile;
......
// 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
......@@ -4,6 +4,7 @@
import {LogReader, parseString, parseVarArgs} from '../logreader.mjs';
import {Profile} from '../profile.mjs';
import {RemoteLinuxCppEntriesProvider, RemoteMacOSCppEntriesProvider} from '../tickprocessor.mjs'
import {ApiLogEntry} from './log/api.mjs';
import {CodeLogEntry, DeoptLogEntry, SharedLibLogEntry} from './log/code.mjs';
......@@ -15,6 +16,37 @@ import {Timeline} from './timeline.mjs';
// ===========================================================================
class AsyncConsumer {
constructor(consumer_fn) {
this._chunks = [];
this._consumer = consumer_fn;
this._pendingWork = Promise.resolve();
this._isConsuming = false;
}
get pendingWork() {
return this._pendingWork;
}
push(chunk) {
this._chunks.push(chunk);
this.consumeAll();
}
async consumeAll() {
if (!this._isConsuming) this._pendingWork = this._consumeAll();
return await this._pendingWork;
}
async _consumeAll() {
this._isConsuming = true;
while (this._chunks.length > 0) {
await this._consumer(this._chunks.shift());
}
this._isConsuming = false;
}
}
export class Processor extends LogReader {
_profile = new Profile();
_apiTimeline = new Timeline();
......@@ -32,6 +64,8 @@ export class Processor extends LogReader {
MINOR_VERSION = 6;
constructor() {
super();
this._chunkConsumer =
new AsyncConsumer((chunk) => this._processChunk(chunk));
const propertyICParser = [
parseInt, parseInt, parseInt, parseInt, parseString, parseString,
parseString, parseString, parseString, parseString
......@@ -149,6 +183,8 @@ export class Processor extends LogReader {
processor: this.processApiEvent
},
};
// TODO(cbruni): Choose correct cpp entries provider
this._cppEntriesProvider = new RemoteLinuxCppEntriesProvider();
}
printError(str) {
......@@ -157,6 +193,10 @@ export class Processor extends LogReader {
}
processChunk(chunk) {
this._chunkConsumer.push(chunk)
}
async _processChunk(chunk) {
let end = chunk.length;
let current = 0;
let next = 0;
......@@ -174,21 +214,21 @@ export class Processor extends LogReader {
this._chunkRemainder = '';
}
current = next + 1;
this.processLogLine(line);
await this.processLogLine(line);
}
} catch (e) {
console.error(`Error occurred during parsing, trying to continue: ${e}`);
}
}
processLogFile(fileName) {
async processLogFile(fileName) {
this.collectEntries = true;
this.lastLogFileName_ = fileName;
let i = 1;
let line;
try {
while (line = readline()) {
this.processLogLine(line);
await this.processLogLine(line);
i++;
}
} catch (e) {
......@@ -199,7 +239,8 @@ export class Processor extends LogReader {
this.finalize();
}
finalize() {
async finalize() {
await this._chunkConsumer.consumeAll();
// TODO(cbruni): print stats;
this._mapTimeline.transitions = new Map();
let id = 0;
......@@ -227,12 +268,16 @@ export class Processor extends LogReader {
}
}
processSharedLibrary(name, start, end, aslr_slide) {
const entry = this._profile.addLibrary(name, start, end);
async processSharedLibrary(name, startAddr, endAddr, aslrSlide) {
const entry = this._profile.addLibrary(name, startAddr, endAddr);
entry.logEntry = new SharedLibLogEntry(entry);
// Many events rely on having a script around, creating fake entries for
// shared libraries.
this._profile.addScriptSource(-1, name, '');
await this._cppEntriesProvider.parseVmSymbols(
name, startAddr, endAddr, aslrSlide, (fName, fStart, fEnd) => {
this._profile.addStaticCode(fName, fStart, fEnd);
});
}
processCodeCreation(type, kind, timestamp, start, size, name, maybe_func) {
......@@ -329,6 +374,7 @@ export class Processor extends LogReader {
this._profile.addSourcePositions(
start, scriptId, startPos, endPos, sourcePositions, inliningPositions,
inlinedFunctions);
if (this._lastCodeLogEntry === undefined) return;
let profileEntry = this._profile.findEntry(start);
if (profileEntry !== this._lastCodeLogEntry._entry) return;
this.addSourcePosition(profileEntry, this._lastCodeLogEntry);
......@@ -383,7 +429,7 @@ export class Processor extends LogReader {
const script = profileEntry.source?.script;
if (script !== undefined) return script;
let fileName;
if (profileEntry.type = 'SHARED_LIB') {
if (profileEntry.type === 'SHARED_LIB') {
fileName = profileEntry.name;
} else {
// Slow path, try to get the script from the url:
......
......@@ -297,6 +297,7 @@ export class TimelineTrackBase extends V8CustomElement {
}
_updateToolTip(event) {
if (!this._focusedEntry) return false;
this.dispatchEvent(
new ToolTipEvent(this._focusedEntry, this.toolTipTargetNode));
event.stopImmediatePropagation();
......@@ -350,7 +351,6 @@ export class TimelineTrackBase extends V8CustomElement {
if (chunk === undefined) return [-1, -1];
const xFrom = (chunk.index * kChunkWidth + kChunkVisualWidth / 2) | 0;
const yFrom = kTimelineHeight - chunk.yOffset(entry) | 0;
console.log(xFrom, yFrom);
return [xFrom, yFrom];
}
......
......@@ -29,7 +29,7 @@ import { ArgumentsProcessor, TickProcessor } from "./tickprocessor.mjs";
const params = ArgumentsProcessor.process(arguments);
const tickProcessor = TickProcessor.fromParams(params);
tickProcessor.processLogFile(params.logFileName);
await tickProcessor.processLogFile(params.logFileName);
if (params.serializeVMSymbols) {
tickProcessor.printVMSymbols();
} else {
......
......@@ -60,12 +60,17 @@ class V8Profile extends Profile {
}
class CppEntriesProvider {
constructor() {
this._isEnabled = true;
}
inRange(funcInfo, start, end) {
return funcInfo.start >= start && funcInfo.end <= end;
}
parseVmSymbols(libName, libStart, libEnd, libASLRSlide, processorFunc) {
this.loadSymbols(libName);
async parseVmSymbols(libName, libStart, libEnd, libASLRSlide, processorFunc) {
if (!this._isEnabled) return;
await this.loadSymbols(libName);
let lastUnknownSize;
let lastAdded;
......@@ -121,12 +126,41 @@ class CppEntriesProvider {
addEntry({ name: '', start: libEnd });
}
loadSymbols(libName) {}
async loadSymbols(libName) {}
async loadSymbolsRemote(platform, libName) {
this.parsePos = 0;
const url = new URL("http://localhost:8000/v8/loadVMSymbols");
url.searchParams.set('libName', libName);
url.searchParams.set('platform', platform);
this._setRemoteQueryParams(url.searchParams);
let response;
let json;
try {
response = await fetch(url);
json = await response.json();
if (json.error) console.warn(json.error);
} catch (e) {
if (!response || response.status == 404) {
// Assume that the local symbol server is not reachable.
console.error("Disabling remote symbol loading:", e);
this._isEnabled = false;
}
}
this._handleRemoteSymbolsResult(json);
}
_setRemoteQueryParams(searchParams) {
// Subclass responsibility.
}
_handleRemoteSymbolsResult(json) {
this.symbols = json.symbols;
}
parseNextLine() { return false }
}
export class LinuxCppEntriesProvider extends CppEntriesProvider {
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
super();
......@@ -142,8 +176,18 @@ export class LinuxCppEntriesProvider extends CppEntriesProvider {
this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
}
_setRemoteQueryParams(searchParams) {
super._setRemoteQueryParams(searchParams);
searchParams.set('targetRootFS', this.targetRootFS ?? "");
searchParams.set('apkEmbeddedLibrary', this.apkEmbeddedLibrary);
}
loadSymbols(libName) {
_handleRemoteSymbolsResult(json) {
super._handleRemoteSymbolsResult(json);
this.fileOffsetMinusVma = json.fileOffsetMinusVma;
}
async loadSymbols(libName) {
this.parsePos = 0;
if (this.apkEmbeddedLibrary && libName.endsWith('.apk')) {
libName = this.apkEmbeddedLibrary;
......@@ -172,9 +216,7 @@ export class LinuxCppEntriesProvider extends CppEntriesProvider {
}
parseNextLine() {
if (this.symbols.length == 0) {
return false;
}
if (this.symbols.length == 0) return false;
const lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
if (lineEndPos == -1) {
this.symbols.shift();
......@@ -196,6 +238,12 @@ export class LinuxCppEntriesProvider extends CppEntriesProvider {
}
}
export class RemoteLinuxCppEntriesProvider extends LinuxCppEntriesProvider {
async loadSymbols(libName) {
return this.loadSymbolsRemote('linux', libName);
}
}
export class MacOSCppEntriesProvider extends LinuxCppEntriesProvider {
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
super(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary);
......@@ -203,14 +251,16 @@ export class MacOSCppEntriesProvider extends LinuxCppEntriesProvider {
this.FUNC_RE = /^([0-9a-fA-F]{8,16})() (.*)$/;
}
loadSymbols(libName) {
async loadSymbols(libName) {
this.parsePos = 0;
libName = this.targetRootFS + libName;
// It seems that in OS X `nm` thinks that `-f` is a format option, not a
// "flat" display option flag.
try {
this.symbols = [os.system(this.nmExec, ['-n', libName], -1, -1), ''];
this.symbols = [
os.system(this.nmExec, ['--demangle', '-n', libName], -1, -1),
''];
} catch (e) {
// If the library cannot be found on this system let's not panic.
this.symbols = '';
......@@ -218,6 +268,12 @@ export class MacOSCppEntriesProvider extends LinuxCppEntriesProvider {
}
}
export class RemoteMacOSCppEntriesProvider extends LinuxCppEntriesProvider {
async loadSymbols(libName) {
return this.loadSymbolsRemote('macos', libName);
}
}
export class WindowsCppEntriesProvider extends CppEntriesProvider {
constructor(_ignored_nmExec, _ignored_objdumpExec, targetRootFS,
......@@ -640,19 +696,19 @@ export class TickProcessor extends LogReader {
return name !== "UNKNOWN" && !(name in this.codeTypes_);
}
processLogFile(fileName) {
async processLogFile(fileName) {
this.lastLogFileName_ = fileName;
let line;
while (line = readline()) {
this.processLogLine(line);
await this.processLogLine(line);
}
}
processLogFileInTest(fileName) {
async processLogFileInTest(fileName) {
// Hack file name to avoid dealing with platform specifics.
this.lastLogFileName_ = 'v8.log';
const contents = d8.file.read(fileName);
this.processLogChunk(contents);
await this.processLogChunk(contents);
}
processSharedLibrary(name, startAddr, endAddr, aslrSlide) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment