Revert "[mjsunit][tools][d8] Full roundtrip tickprocessor test"
This reverts commit ed7e4554db
.
Reason for revert: new test fails on Mac: https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Mac64/40407/overview
Original change's description:
> [mjsunit][tools][d8] Full roundtrip tickprocessor test
>
> - Add os.d8Path property
> - Add os.name property
> - Change tickprocssor test to use command line arguments for testing
> various configurations
> - Change tickprocessor test to create a temporary v8.log and read it
> back in on linux only
> - Rearrange code in tickprocessor.mjs to allow instantiating the
> CppEntriesProvider directly
> - Drop complete symbol-list for tickprocessor-test-large.log for better
> code searching in V8
>
> Change-Id: Ib56dd0a1ba5377282c84c4de6f17e2fd69ee8123
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2929120
> Reviewed-by: Patrick Thier <pthier@chromium.org>
> Commit-Queue: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#74892}
Change-Id: I7d7506b370f96365552a21fa767b1c5c608ebb1c
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2929380
Auto-Submit: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/master@{#74894}
This commit is contained in:
parent
705527f839
commit
72564ba765
19
src/d8/d8.cc
19
src/d8/d8.cc
@ -2800,24 +2800,6 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
|
||||
Local<ObjectTemplate> Shell::CreateOSTemplate(Isolate* isolate) {
|
||||
Local<ObjectTemplate> os_template = ObjectTemplate::New(isolate);
|
||||
AddOSMethods(isolate, os_template);
|
||||
#if V8_TARGET_OS_LINUX
|
||||
const char os_name[] = "linux";
|
||||
#elif V8_TARGET_OS_WIN
|
||||
const char os_name[] = "windows";
|
||||
#elif V8_TARGET_OS_MACOSX
|
||||
const char os_name[] = "macos";
|
||||
#elif V8_TARGET_OS_ANDROID
|
||||
const char os_name[] = "android";
|
||||
#else
|
||||
const char os_name[] = "unknown";
|
||||
#endif
|
||||
os_template->Set(isolate, "name",
|
||||
v8::String::NewFromUtf8Literal(isolate, os_name),
|
||||
PropertyAttribute::ReadOnly);
|
||||
os_template->Set(
|
||||
isolate, "d8Path",
|
||||
v8::String::NewFromUtf8(isolate, options.d8_path).ToLocalChecked(),
|
||||
PropertyAttribute::ReadOnly);
|
||||
return os_template;
|
||||
}
|
||||
|
||||
@ -4135,7 +4117,6 @@ void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
bool logfile_per_isolate = false;
|
||||
bool no_always_opt = false;
|
||||
options.d8_path = argv[0];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--") == 0) {
|
||||
argv[i] = nullptr;
|
||||
|
20
src/d8/d8.h
20
src/d8/d8.h
@ -351,7 +351,6 @@ class ShellOptions {
|
||||
bool specified_ = false;
|
||||
};
|
||||
|
||||
DisallowReassignment<const char*> d8_path = {"d8-path", ""};
|
||||
DisallowReassignment<bool> fuzzilli_coverage_statistics = {
|
||||
"fuzzilli-coverage-statistics", false};
|
||||
DisallowReassignment<bool> fuzzilli_enable_builtins_coverage = {
|
||||
@ -542,30 +541,29 @@ class Shell : public i::AllStatic {
|
||||
// milliseconds on the total running time of the program. Exceptions are
|
||||
// thrown on timeouts or other errors or if the exit status of the program
|
||||
// indicates an error.
|
||||
static void System(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
//
|
||||
// os.chdir(dir) changes directory to the given directory. Throws an
|
||||
// exception/ on error.
|
||||
static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
//
|
||||
// os.setenv(variable, value) sets an environment variable. Repeated calls to
|
||||
// this method leak memory due to the API of setenv in the standard C library.
|
||||
static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
//
|
||||
// os.umask(alue) calls the umask system call and returns the old umask.
|
||||
static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
//
|
||||
// os.mkdirp(name, mask) creates a directory. The mask (if present) is anded
|
||||
// with the current umask. Intermediate directories are created if necessary.
|
||||
// An exception is not thrown if the directory already exists. Analogous to
|
||||
// the "mkdir -p" command.
|
||||
static void System(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static MaybeLocal<Promise> HostImportModuleDynamically(
|
||||
Local<Context> context, Local<ScriptOrModule> referrer,
|
||||
Local<String> specifier, Local<FixedArray> import_assertions);
|
||||
|
||||
static void ModuleResolutionSuccessCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void ModuleResolutionFailureCallback(
|
||||
|
@ -41,10 +41,6 @@ function arg_error(str) {
|
||||
}
|
||||
}
|
||||
|
||||
const kOsNameValues = ['linux', 'macos', 'windows', 'android', 'unknown'];
|
||||
assertTrue(kOsNameValues.includes(os.name));
|
||||
|
||||
assertTrue(os.d8Path.length > 0);
|
||||
|
||||
function str_error(str) {
|
||||
var e = new Object();
|
||||
|
@ -2,30 +2,49 @@ Statistical profiling result from v8.log, (303 ticks, 0 unaccounted, 0 excluded)
|
||||
|
||||
[Shared libraries]:
|
||||
ticks total nonlib name
|
||||
24 7.9% /usr/local/google/home/cbruni/Documents/v8/v8/out/x64.release/d8
|
||||
10 3.3% /usr/lib/x86_64-linux-gnu/libc-2.31.so
|
||||
4 1.3% /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
|
||||
9 3.0% /usr/local/google/home/cbruni/Documents/v8/v8/out/x64.release/d8
|
||||
6 2.0% /usr/lib/x86_64-linux-gnu/libc-2.31.so
|
||||
|
||||
[JavaScript]:
|
||||
ticks total nonlib name
|
||||
102 33.7% 38.5% LazyCompile: *<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
25 8.3% 9.4% LazyCompile: *ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
21 6.9% 7.9% LazyCompile: *loop test/mjsunit/tools/tickprocessor-test.js:10:14
|
||||
102 33.7% 35.4% LazyCompile: *<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
25 8.3% 8.7% LazyCompile: *ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
21 6.9% 7.3% LazyCompile: *loop test/mjsunit/tools/tickprocessor-test.js:10:14
|
||||
|
||||
[C++]:
|
||||
ticks total nonlib name
|
||||
99 32.7% 37.4% v8_Default_embedded_blob_code_data_
|
||||
12 4.0% 4.5% __write
|
||||
3 1.0% 1.1% fwrite
|
||||
2 0.7% 0.8% v8::internal::RootScavengeVisitor::VisitRootPointer(v8::internal::Root, char const*, v8::internal::FullObjectSlot)
|
||||
1 0.3% 0.4% v8::internal::compiler::TopLevelLiveRange::AddUseInterval(v8::internal::compiler::LifetimePosition, v8::internal::compiler::LifetimePosition, v8::internal::Zone*, bool)
|
||||
99 32.7% 34.4% v8_Default_embedded_blob_code_data_
|
||||
12 4.0% 4.2% __write
|
||||
3 1.0% 1.0% fwrite
|
||||
2 0.7% 0.7% v8::internal::RootScavengeVisitor::VisitRootPointer(v8::internal::Root, char const*, v8::internal::FullObjectSlot)
|
||||
2 0.7% 0.7% __pthread_mutex_unlock_usercnt
|
||||
2 0.7% 0.7% __libc_malloc
|
||||
2 0.7% 0.7% _IO_file_xsputn
|
||||
1 0.3% 0.3% v8::internal::compiler::TopLevelLiveRange::AddUseInterval(v8::internal::compiler::LifetimePosition, v8::internal::compiler::LifetimePosition, v8::internal::Zone*, bool)
|
||||
1 0.3% 0.3% v8::internal::compiler::Scheduler::PrepareUses()
|
||||
1 0.3% 0.3% v8::internal::compiler::CommonOperatorBuilder::CommonOperatorBuilder(v8::internal::Zone*)
|
||||
1 0.3% 0.3% v8::internal::SpaceWithLinearArea::AdvanceAllocationObservers()
|
||||
1 0.3% 0.3% v8::internal::SerializerDeserializer::Iterate(v8::internal::Isolate*, v8::internal::RootVisitor*)
|
||||
1 0.3% 0.3% v8::internal::Log::MessageBuilder::AppendString(char const*, unsigned long, bool)
|
||||
1 0.3% 0.3% v8::internal::LargeObjectSpace::SizeOfObjects()
|
||||
1 0.3% 0.3% v8::internal::Heap::Scavenge()
|
||||
1 0.3% 0.3% v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment)
|
||||
1 0.3% 0.3% v8::internal::Heap::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment)
|
||||
1 0.3% 0.3% v8::OutputStream::GetChunkSize()
|
||||
1 0.3% 0.3% std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > std::__1::__pad_and_output<char, std::__1::char_traits<char> >(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, char const*, char const*, char const*, std::__1::ios_base&, char)
|
||||
1 0.3% 0.3% std::__1::num_put<char, std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > >::do_put(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, char, long) const
|
||||
1 0.3% 0.3% std::__1::basic_ostream<char, std::__1::char_traits<char> >::operator<<(unsigned long)
|
||||
1 0.3% 0.3% int v8::internal::Deserializer::ReadSingleBytecodeData<v8::internal::SlotAccessorForHeapObject>(unsigned char, v8::internal::SlotAccessorForHeapObject)
|
||||
1 0.3% 0.3% __pthread_cond_wait
|
||||
1 0.3% 0.3% __pthread_cond_signal
|
||||
1 0.3% 0.3% Cr_z_inflate_fast_chunk_
|
||||
|
||||
[Summary]:
|
||||
ticks total nonlib name
|
||||
148 48.8% 55.8% JavaScript
|
||||
117 38.6% 44.2% C++
|
||||
20 6.6% 7.5% GC
|
||||
38 12.5% Shared libraries
|
||||
148 48.8% 51.4% JavaScript
|
||||
140 46.2% 48.6% C++
|
||||
20 6.6% 6.9% GC
|
||||
15 5.0% Shared libraries
|
||||
|
||||
[C++ entry points]:
|
||||
ticks cpp total name
|
||||
@ -50,25 +69,17 @@ Statistical profiling result from v8.log, (303 ticks, 0 unaccounted, 0 excluded)
|
||||
25 8.3% LazyCompile: *ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
25 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
|
||||
24 7.9% /usr/local/google/home/cbruni/Documents/v8/v8/out/x64.release/d8
|
||||
4 16.7% LazyCompile: ~ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
4 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
3 12.5% v8::internal::Runtime_CompileForOnStackReplacement(int, unsigned long*, v8::internal::Isolate*)
|
||||
2 66.7% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
1 33.3% LazyCompile: ~loop test/mjsunit/tools/tickprocessor-test.js:10:14
|
||||
1 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
2 8.3% v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*)
|
||||
2 100.0% LazyCompile: *<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
2 8.3% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
2 8.3% LazyCompile: ~read_megamorphic test/mjsunit/tools/tickprocessor-test.js:35:26
|
||||
2 100.0% LazyCompile: ~ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
2 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
|
||||
21 6.9% LazyCompile: *loop test/mjsunit/tools/tickprocessor-test.js:10:14
|
||||
21 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
|
||||
12 4.0% __write
|
||||
|
||||
10 3.3% /usr/lib/x86_64-linux-gnu/libc-2.31.so
|
||||
9 3.0% /usr/local/google/home/cbruni/Documents/v8/v8/out/x64.release/d8
|
||||
4 44.4% LazyCompile: ~ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
4 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
2 22.2% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
2 22.2% LazyCompile: ~read_megamorphic test/mjsunit/tools/tickprocessor-test.js:35:26
|
||||
2 100.0% LazyCompile: ~ics test/mjsunit/tools/tickprocessor-test.js:47:13
|
||||
2 100.0% Script: ~<anonymous> test/mjsunit/tools/tickprocessor-test.js:1:1
|
||||
|
||||
4 1.3% /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
|
||||
6 2.0% /usr/lib/x86_64-linux-gnu/libc-2.31.so
|
||||
|
@ -53,6 +53,6 @@ function ics() {
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10_000; i++) {
|
||||
for (let i = 0; i < 100_000; i++) {
|
||||
ics();
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -25,11 +25,9 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --enable-os-system
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-func-info.log
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-func-info.log.symbols.json
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-large.default
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-large.js
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-large.log
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test-large.log.symbols.json
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test.default
|
||||
@ -42,12 +40,23 @@
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test.separate-baseline-handlers
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test.separate-bytecodes
|
||||
// Resources: test/mjsunit/tools/tickprocessor-test.separate-ic
|
||||
// Env: TEST_FILE_NAME
|
||||
|
||||
import {
|
||||
TickProcessor, ArgumentsProcessor, UnixCppEntriesProvider,
|
||||
MacCppEntriesProvider, WindowsCppEntriesProvider
|
||||
MacCppEntriesProvider, WindowsCppEntriesProvider
|
||||
} from "../../../tools/tickprocessor.mjs";
|
||||
|
||||
// TODO(cbruni): Remove after refactoring
|
||||
export function readFile(fileName) {
|
||||
try {
|
||||
return read(fileName);
|
||||
} catch (e) {
|
||||
console.log(fileName + ': ' + (e.message || e));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
(function testArgumentsProcessor() {
|
||||
var p_default = new ArgumentsProcessor([]);
|
||||
assertTrue(p_default.parse());
|
||||
@ -363,7 +372,7 @@ import {
|
||||
class CppEntriesProviderMock {
|
||||
constructor(filename) {
|
||||
this.isLoaded = false;
|
||||
this.symbols = JSON.parse(d8.file.read(filename));
|
||||
this.symbols = JSON.parse(readFile(filename));
|
||||
}
|
||||
parseVmSymbols(name, startAddr, endAddr, slideAddr, symbolAdder) {
|
||||
if (this.isLoaded) return;
|
||||
@ -383,17 +392,17 @@ class PrintMonitor {
|
||||
this.expectedOut = this.loadExpectedOutput(outputOrFileName)
|
||||
this.outputFile = outputOrFileName;
|
||||
}
|
||||
let expectedOut = this.expectedOut;
|
||||
let outputPos = 0;
|
||||
let diffs = this.diffs = [];
|
||||
let realOut = this.realOut = [];
|
||||
let unexpectedOut = this.unexpectedOut = null;
|
||||
var expectedOut = this.expectedOut;
|
||||
var outputPos = 0;
|
||||
var diffs = this.diffs = [];
|
||||
var realOut = this.realOut = [];
|
||||
var unexpectedOut = this.unexpectedOut = null;
|
||||
|
||||
this.oldPrint = print;
|
||||
print = function(str) {
|
||||
const strSplit = str.split('\n');
|
||||
for (let i = 0; i < strSplit.length; ++i) {
|
||||
const s = strSplit[i];
|
||||
var strSplit = str.split('\n');
|
||||
for (var i = 0; i < strSplit.length; ++i) {
|
||||
var s = strSplit[i];
|
||||
realOut.push(s);
|
||||
if (outputPos < expectedOut.length) {
|
||||
if (expectedOut[outputPos] != s) {
|
||||
@ -409,95 +418,98 @@ class PrintMonitor {
|
||||
}
|
||||
|
||||
loadExpectedOutput(fileName) {
|
||||
const output = d8.file.read(fileName);
|
||||
var output = readFile(fileName);
|
||||
return output.split('\n');
|
||||
}
|
||||
|
||||
finish() {
|
||||
print = this.oldPrint;
|
||||
if (this.diffs.length == 0 && this.unexpectedOut == null) return;
|
||||
console.log("===== actual output: =====");
|
||||
console.log(this.realOut.join('\n'));
|
||||
console.log("===== expected output: =====");
|
||||
if (this.outputFile) {
|
||||
console.log("===== File: " + this.outputFile + " =====");
|
||||
if (this.diffs.length > 0 || this.unexpectedOut != null) {
|
||||
console.log("===== actual output: =====");
|
||||
console.log(this.realOut.join('\n'));
|
||||
console.log("===== expected output: =====");
|
||||
if (this.outputFile) {
|
||||
console.log("===== File: " + this.outputFile + " =====");
|
||||
}
|
||||
console.log(this.expectedOut.join('\n'));
|
||||
if (this.diffs.length > 0) {
|
||||
this.diffs.forEach(line => console.log(line))
|
||||
assertEquals([], this.diffs);
|
||||
}
|
||||
assertNull(this.unexpectedOut);
|
||||
}
|
||||
console.log(this.expectedOut.join('\n'));
|
||||
if (this.diffs.length > 0) {
|
||||
this.diffs.forEach(line => console.log(line))
|
||||
assertEquals([], this.diffs);
|
||||
}
|
||||
assertNull(this.unexpectedOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function driveTickProcessorTest(
|
||||
separateIc, separateBytecodes, separateBuiltins, separateStubs,
|
||||
separateBaselineHandlers, ignoreUnknown, stateFilter, logInput,
|
||||
refOutput, onlySummary) {
|
||||
// TEST_FILE_NAME must be provided by test runner.
|
||||
assertEquals('string', typeof TEST_FILE_NAME);
|
||||
var pathLen = TEST_FILE_NAME.lastIndexOf('/');
|
||||
if (pathLen == -1) {
|
||||
pathLen = TEST_FILE_NAME.lastIndexOf('\\');
|
||||
}
|
||||
assertTrue(pathLen != -1);
|
||||
const testsPath = TEST_FILE_NAME.substr(0, pathLen + 1);
|
||||
const symbolsFile = testsPath + logInput + '.symbols.json';
|
||||
const tp = new TickProcessor(new CppEntriesProviderMock(symbolsFile),
|
||||
separateIc,
|
||||
separateBytecodes,
|
||||
separateBuiltins,
|
||||
separateStubs,
|
||||
separateBaselineHandlers,
|
||||
TickProcessor.CALL_GRAPH_SIZE,
|
||||
ignoreUnknown,
|
||||
stateFilter,
|
||||
"0",
|
||||
"auto,auto",
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
onlySummary);
|
||||
const pm = new PrintMonitor(testsPath + refOutput);
|
||||
tp.processLogFileInTest(testsPath + logInput);
|
||||
tp.printStatistics();
|
||||
pm.finish();
|
||||
};
|
||||
|
||||
|
||||
(function testProcessing() {
|
||||
const testData = {
|
||||
var testData = {
|
||||
'Default': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.default',
|
||||
['--separate-ic=false']],
|
||||
false, false, true, true, false, false, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.default', false],
|
||||
'SeparateBytecodes': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-bytecodes',
|
||||
['--separate-ic=false', '--separate-bytecodes']],
|
||||
false, true, true, true, false, false, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-bytecodes', false],
|
||||
'SeparateBaselineHandlers': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-baseline-handlers',
|
||||
['--separate-ic=false', '--separate-baseline-handlers']],
|
||||
false, false, true, true, true, false, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-baseline-handlers', false],
|
||||
'SeparateIc': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-ic',
|
||||
['--separate-ic=true']],
|
||||
true, false, true, true, false, false, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.separate-ic', false],
|
||||
'IgnoreUnknown': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.ignore-unknown',
|
||||
['--separate-ic=false', '--ignore-unknown']],
|
||||
false, false, true, true, false, true, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.ignore-unknown', false],
|
||||
'GcState': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.gc-state', ['-g']],
|
||||
false, false, true, true, false, false, TickProcessor.VmStates.GC,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.gc-state', false],
|
||||
'OnlySummary': [
|
||||
'tickprocessor-test.log', 'tickprocessor-test.only-summary',
|
||||
['--separate-ic=false', '--only-summary']],
|
||||
false, false, true, true, false, false, null,
|
||||
'tickprocessor-test.log', 'tickprocessor-test.only-summary', true],
|
||||
'FunctionInfo': [
|
||||
false, false, true, true, false, false, null,
|
||||
'tickprocessor-test-func-info.log', 'tickprocessor-test.func-info',
|
||||
['']
|
||||
],
|
||||
false],
|
||||
'DefaultLarge': [
|
||||
'tickprocessor-test-large.log', 'tickprocessor-test-large.default'],
|
||||
false, false, true, true, false, false, null,
|
||||
'tickprocessor-test-large.log', 'tickprocessor-test-large.default', false],
|
||||
};
|
||||
for (var testName in testData) {
|
||||
console.log('=== testProcessing-' + testName + ' ===');
|
||||
testTickProcessor(...testData[testName]);
|
||||
driveTickProcessorTest(...testData[testName]);
|
||||
}
|
||||
})();
|
||||
|
||||
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, dir + refOutput, params);
|
||||
testEndToEnd(dir + 'tickprocessor-test-large.js', dir + refOutput, params);
|
||||
}
|
||||
|
||||
function testExpectations(logInput, refOutput, params) {
|
||||
const symbolsFile = logInput + '.symbols.json';
|
||||
const cppEntries = new CppEntriesProviderMock(symbolsFile);
|
||||
const tickProcessor = TickProcessor.fromParams(params, cppEntries);
|
||||
const printMonitor = new PrintMonitor(refOutput);
|
||||
tickProcessor.processLogFileInTest(logInput);
|
||||
tickProcessor.printStatistics();
|
||||
printMonitor.finish();
|
||||
};
|
||||
|
||||
function testEndToEnd(sourceFile, ignoredRefOutput, params) {
|
||||
// This test only works on linux.
|
||||
if (!os?.system) return;
|
||||
if (os.name !== 'linux' && os.name !== 'macos') return;
|
||||
params.platform = os.name;
|
||||
const tmpLogFile= `/var/tmp/${Date.now()}.v8.log`
|
||||
const result = os.system(
|
||||
os.d8Path, ['--prof', `--logfile=${tmpLogFile}`, sourceFile]);
|
||||
|
||||
const tickProcessor = TickProcessor.fromParams(params);
|
||||
// We will not always get the same ticks due to timing on bots,
|
||||
// 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(ignoredRefOutput);
|
||||
tickProcessor.processLogFileInTest(tmpLogFile);
|
||||
tickProcessor.printStatistics();
|
||||
}
|
@ -25,11 +25,41 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import { ArgumentsProcessor, TickProcessor } from "./tickprocessor.mjs";
|
||||
import {
|
||||
ArgumentsProcessor, TickProcessor, UnixCppEntriesProvider,
|
||||
WindowsCppEntriesProvider, MacCppEntriesProvider
|
||||
} from "./tickprocessor.mjs";
|
||||
|
||||
// Tick Processor's code flow.
|
||||
|
||||
const entriesProviders = {
|
||||
'unix': UnixCppEntriesProvider,
|
||||
'windows': WindowsCppEntriesProvider,
|
||||
'mac': MacCppEntriesProvider
|
||||
};
|
||||
|
||||
const params = ArgumentsProcessor.process(arguments);
|
||||
const tickProcessor = TickProcessor.fromParams(params);
|
||||
const tickProcessor = new TickProcessor(
|
||||
new (entriesProviders[params.platform])(params.nm, params.objdump, params.targetRootFS,
|
||||
params.apkEmbeddedLibrary),
|
||||
params.separateIc,
|
||||
params.separateBytecodes,
|
||||
params.separateBuiltins,
|
||||
params.separateStubs,
|
||||
params.separateBaselineHandlers,
|
||||
params.callGraphSize,
|
||||
params.ignoreUnknown,
|
||||
params.stateFilter,
|
||||
params.distortion,
|
||||
params.range,
|
||||
params.sourceMap,
|
||||
params.timedRange,
|
||||
params.pairwiseTimedRange,
|
||||
params.onlySummary,
|
||||
params.runtimeTimerFilter,
|
||||
params.preprocessJson);
|
||||
tickProcessor.processLogFile(params.logFileName);
|
||||
|
||||
if (params.serializeVMSymbols) {
|
||||
tickProcessor.printVMSymbols();
|
||||
} else {
|
||||
|
@ -59,378 +59,8 @@ class V8Profile extends Profile {
|
||||
}
|
||||
}
|
||||
|
||||
class CppEntriesProvider {
|
||||
inRange(funcInfo, start, end) {
|
||||
return funcInfo.start >= start && funcInfo.end <= end;
|
||||
}
|
||||
|
||||
parseVmSymbols(libName, libStart, libEnd, libASLRSlide, processorFunc) {
|
||||
this.loadSymbols(libName);
|
||||
|
||||
let lastUnknownSize;
|
||||
let lastAdded;
|
||||
|
||||
let addEntry = (funcInfo) => {
|
||||
// Several functions can be mapped onto the same address. To avoid
|
||||
// creating zero-sized entries, skip such duplicates.
|
||||
// Also double-check that function belongs to the library address space.
|
||||
|
||||
if (lastUnknownSize &&
|
||||
lastUnknownSize.start < funcInfo.start) {
|
||||
// Try to update lastUnknownSize based on new entries start position.
|
||||
lastUnknownSize.end = funcInfo.start;
|
||||
if ((!lastAdded ||
|
||||
!this.inRange(lastUnknownSize, lastAdded.start, lastAdded.end)) &&
|
||||
this.inRange(lastUnknownSize, libStart, libEnd)) {
|
||||
processorFunc(
|
||||
lastUnknownSize.name, lastUnknownSize.start, lastUnknownSize.end);
|
||||
lastAdded = lastUnknownSize;
|
||||
}
|
||||
}
|
||||
lastUnknownSize = undefined;
|
||||
|
||||
if (funcInfo.end) {
|
||||
// Skip duplicates that have the same start address as the last added.
|
||||
if ((!lastAdded || lastAdded.start != funcInfo.start) &&
|
||||
this.inRange(funcInfo, libStart, libEnd)) {
|
||||
processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
|
||||
lastAdded = funcInfo;
|
||||
}
|
||||
} else {
|
||||
// If a funcInfo doesn't have an end, try to match it up with the next
|
||||
// entry.
|
||||
lastUnknownSize = funcInfo;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const funcInfo = this.parseNextLine();
|
||||
if (funcInfo === null) continue;
|
||||
if (funcInfo === false) break;
|
||||
if (funcInfo.start < libStart - libASLRSlide &&
|
||||
funcInfo.start < libEnd - libStart) {
|
||||
funcInfo.start += libStart;
|
||||
} else {
|
||||
funcInfo.start += libASLRSlide;
|
||||
}
|
||||
if (funcInfo.size) {
|
||||
funcInfo.end = funcInfo.start + funcInfo.size;
|
||||
}
|
||||
addEntry(funcInfo);
|
||||
}
|
||||
addEntry({ name: '', start: libEnd });
|
||||
}
|
||||
|
||||
loadSymbols(libName) {}
|
||||
|
||||
parseNextLine() { return false }
|
||||
}
|
||||
|
||||
|
||||
export class UnixCppEntriesProvider extends CppEntriesProvider {
|
||||
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
|
||||
super();
|
||||
this.symbols = [];
|
||||
// File offset of a symbol minus the virtual address of a symbol found in
|
||||
// the symbol table.
|
||||
this.fileOffsetMinusVma = 0;
|
||||
this.parsePos = 0;
|
||||
this.nmExec = nmExec;
|
||||
this.objdumpExec = objdumpExec;
|
||||
this.targetRootFS = targetRootFS;
|
||||
this.apkEmbeddedLibrary = apkEmbeddedLibrary;
|
||||
this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
|
||||
}
|
||||
|
||||
|
||||
loadSymbols(libName) {
|
||||
this.parsePos = 0;
|
||||
if (this.apkEmbeddedLibrary && libName.endsWith('.apk')) {
|
||||
libName = this.apkEmbeddedLibrary;
|
||||
}
|
||||
if (this.targetRootFS) {
|
||||
libName = libName.substring(libName.lastIndexOf('/') + 1);
|
||||
libName = this.targetRootFS + libName;
|
||||
}
|
||||
try {
|
||||
this.symbols = [
|
||||
os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
|
||||
os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
|
||||
];
|
||||
|
||||
const objdumpOutput = os.system(this.objdumpExec, ['-h', libName], -1, -1);
|
||||
for (const line of objdumpOutput.split('\n')) {
|
||||
const [, sectionName, , vma, , fileOffset] = line.trim().split(/\s+/);
|
||||
if (sectionName === ".text") {
|
||||
this.fileOffsetMinusVma = parseInt(fileOffset, 16) - parseInt(vma, 16);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If the library cannot be found on this system let's not panic.
|
||||
this.symbols = ['', ''];
|
||||
}
|
||||
}
|
||||
|
||||
parseNextLine() {
|
||||
if (this.symbols.length == 0) {
|
||||
return false;
|
||||
}
|
||||
const lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
|
||||
if (lineEndPos == -1) {
|
||||
this.symbols.shift();
|
||||
this.parsePos = 0;
|
||||
return this.parseNextLine();
|
||||
}
|
||||
|
||||
const line = this.symbols[0].substring(this.parsePos, lineEndPos);
|
||||
this.parsePos = lineEndPos + 1;
|
||||
const fields = line.match(this.FUNC_RE);
|
||||
let funcInfo = null;
|
||||
if (fields) {
|
||||
funcInfo = { name: fields[3], start: parseInt(fields[1], 16) + this.fileOffsetMinusVma };
|
||||
if (fields[2]) {
|
||||
funcInfo.size = parseInt(fields[2], 16);
|
||||
}
|
||||
}
|
||||
return funcInfo;
|
||||
}
|
||||
}
|
||||
|
||||
export class MacCppEntriesProvider extends UnixCppEntriesProvider {
|
||||
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
|
||||
super(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary);
|
||||
// Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
|
||||
this.FUNC_RE = /^([0-9a-fA-F]{8,16})() (.*)$/;
|
||||
}
|
||||
|
||||
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), ''];
|
||||
} catch (e) {
|
||||
// If the library cannot be found on this system let's not panic.
|
||||
this.symbols = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class WindowsCppEntriesProvider extends CppEntriesProvider {
|
||||
constructor(_ignored_nmExec, _ignored_objdumpExec, targetRootFS,
|
||||
_ignored_apkEmbeddedLibrary) {
|
||||
super();
|
||||
this.targetRootFS = targetRootFS;
|
||||
this.symbols = '';
|
||||
this.parsePos = 0;
|
||||
}
|
||||
|
||||
static FILENAME_RE = /^(.*)\.([^.]+)$/;
|
||||
static FUNC_RE =
|
||||
/^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
|
||||
static IMAGE_BASE_RE =
|
||||
/^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
|
||||
// This is almost a constant on Windows.
|
||||
static EXE_IMAGE_BASE = 0x00400000;
|
||||
|
||||
loadSymbols(libName) {
|
||||
libName = this.targetRootFS + libName;
|
||||
const fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
|
||||
if (!fileNameFields) return;
|
||||
const mapFileName = `${fileNameFields[1]}.map`;
|
||||
this.moduleType_ = fileNameFields[2].toLowerCase();
|
||||
try {
|
||||
this.symbols = read(mapFileName);
|
||||
} catch (e) {
|
||||
// If .map file cannot be found let's not panic.
|
||||
this.symbols = '';
|
||||
}
|
||||
}
|
||||
|
||||
parseNextLine() {
|
||||
const lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
|
||||
if (lineEndPos == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const line = this.symbols.substring(this.parsePos, lineEndPos);
|
||||
this.parsePos = lineEndPos + 2;
|
||||
|
||||
// Image base entry is above all other symbols, so we can just
|
||||
// terminate parsing.
|
||||
const imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
|
||||
if (imageBaseFields) {
|
||||
const imageBase = parseInt(imageBaseFields[1], 16);
|
||||
if ((this.moduleType_ == 'exe') !=
|
||||
(imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
|
||||
return fields ?
|
||||
{ name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs very simple unmangling of C++ names.
|
||||
*
|
||||
* Does not handle arguments and template arguments. The mangled names have
|
||||
* the form:
|
||||
*
|
||||
* ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
|
||||
*/
|
||||
unmangleName(name) {
|
||||
// Empty or non-mangled name.
|
||||
if (name.length < 1 || name.charAt(0) != '?') return name;
|
||||
const nameEndPos = name.indexOf('@@');
|
||||
const components = name.substring(1, nameEndPos).split('@');
|
||||
components.reverse();
|
||||
return components.join('::');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ArgumentsProcessor extends BaseArgumentsProcessor {
|
||||
getArgsDispatch() {
|
||||
let dispatch = {
|
||||
__proto__:null,
|
||||
'-j': ['stateFilter', TickProcessor.VmStates.JS,
|
||||
'Show only ticks from JS VM state'],
|
||||
'-g': ['stateFilter', TickProcessor.VmStates.GC,
|
||||
'Show only ticks from GC VM state'],
|
||||
'-p': ['stateFilter', TickProcessor.VmStates.PARSER,
|
||||
'Show only ticks from PARSER VM state'],
|
||||
'-b': ['stateFilter', TickProcessor.VmStates.BYTECODE_COMPILER,
|
||||
'Show only ticks from BYTECODE_COMPILER VM state'],
|
||||
'-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
|
||||
'Show only ticks from COMPILER VM state'],
|
||||
'-o': ['stateFilter', TickProcessor.VmStates.OTHER,
|
||||
'Show only ticks from OTHER VM state'],
|
||||
'-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
|
||||
'Show only ticks from EXTERNAL VM state'],
|
||||
'--filter-runtime-timer': ['runtimeTimerFilter', null,
|
||||
'Show only ticks matching the given runtime timer scope'],
|
||||
'--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
|
||||
'Set the call graph size'],
|
||||
'--ignore-unknown': ['ignoreUnknown', true,
|
||||
'Exclude ticks of unknown code entries from processing'],
|
||||
'--separate-ic': ['separateIc', parseBool,
|
||||
'Separate IC entries'],
|
||||
'--separate-bytecodes': ['separateBytecodes', parseBool,
|
||||
'Separate Bytecode entries'],
|
||||
'--separate-builtins': ['separateBuiltins', parseBool,
|
||||
'Separate Builtin entries'],
|
||||
'--separate-stubs': ['separateStubs', parseBool,
|
||||
'Separate Stub entries'],
|
||||
'--separate-baseline-handlers': ['separateBaselineHandlers', parseBool,
|
||||
'Separate Baseline Handler entries'],
|
||||
'--linux': ['platform', 'linux',
|
||||
'Specify that we are running on *nix platform'],
|
||||
'--windows': ['platform', 'windows',
|
||||
'Specify that we are running on Windows platform'],
|
||||
'--mac': ['platform', 'mac',
|
||||
'Specify that we are running on Mac OS X platform'],
|
||||
'--nm': ['nm', 'nm',
|
||||
'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
|
||||
'--objdump': ['objdump', 'objdump',
|
||||
'Specify the \'objdump\' executable to use (e.g. --objdump=/my_dir/objdump)'],
|
||||
'--target': ['targetRootFS', '',
|
||||
'Specify the target root directory for cross environment'],
|
||||
'--apk-embedded-library': ['apkEmbeddedLibrary', '',
|
||||
'Specify the path of the embedded library for Android traces'],
|
||||
'--range': ['range', 'auto,auto',
|
||||
'Specify the range limit as [start],[end]'],
|
||||
'--distortion': ['distortion', 0,
|
||||
'Specify the logging overhead in picoseconds'],
|
||||
'--source-map': ['sourceMap', null,
|
||||
'Specify the source map that should be used for output'],
|
||||
'--timed-range': ['timedRange', true,
|
||||
'Ignore ticks before first and after last Date.now() call'],
|
||||
'--pairwise-timed-range': ['pairwiseTimedRange', true,
|
||||
'Ignore ticks outside pairs of Date.now() calls'],
|
||||
'--only-summary': ['onlySummary', true,
|
||||
'Print only tick summary, exclude other information'],
|
||||
'--serialize-vm-symbols': ['serializeVMSymbols', true,
|
||||
'Print all C++ symbols and library addresses as JSON data'],
|
||||
'--preprocess': ['preprocessJson', true,
|
||||
'Preprocess for consumption with web interface']
|
||||
};
|
||||
dispatch['--js'] = dispatch['-j'];
|
||||
dispatch['--gc'] = dispatch['-g'];
|
||||
dispatch['--compiler'] = dispatch['-c'];
|
||||
dispatch['--other'] = dispatch['-o'];
|
||||
dispatch['--external'] = dispatch['-e'];
|
||||
dispatch['--ptr'] = dispatch['--pairwise-timed-range'];
|
||||
return dispatch;
|
||||
}
|
||||
|
||||
getDefaultResults() {
|
||||
return {
|
||||
logFileName: 'v8.log',
|
||||
platform: 'linux',
|
||||
stateFilter: null,
|
||||
callGraphSize: 5,
|
||||
ignoreUnknown: false,
|
||||
separateIc: true,
|
||||
separateBytecodes: false,
|
||||
separateBuiltins: true,
|
||||
separateStubs: true,
|
||||
separateBaselineHandlers: false,
|
||||
preprocessJson: null,
|
||||
sourceMap: null,
|
||||
targetRootFS: '',
|
||||
nm: 'nm',
|
||||
objdump: 'objdump',
|
||||
range: 'auto,auto',
|
||||
distortion: 0,
|
||||
timedRange: false,
|
||||
pairwiseTimedRange: false,
|
||||
onlySummary: false,
|
||||
runtimeTimerFilter: null,
|
||||
serializeVMSymbols: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class TickProcessor extends LogReader {
|
||||
static EntriesProvider = {
|
||||
'linux': UnixCppEntriesProvider,
|
||||
'windows': WindowsCppEntriesProvider,
|
||||
'mac': MacCppEntriesProvider
|
||||
};
|
||||
|
||||
static fromParams(params, entriesProvider) {
|
||||
if (entriesProvider == undefined) {
|
||||
entriesProvider = new this.EntriesProvider[params.platform](
|
||||
params.nm, params.objdump, params.targetRootFS,
|
||||
params.apkEmbeddedLibrary);
|
||||
}
|
||||
return new TickProcessor(
|
||||
entriesProvider,
|
||||
params.separateIc,
|
||||
params.separateBytecodes,
|
||||
params.separateBuiltins,
|
||||
params.separateStubs,
|
||||
params.separateBaselineHandlers,
|
||||
params.callGraphSize,
|
||||
params.ignoreUnknown,
|
||||
params.stateFilter,
|
||||
params.distortion,
|
||||
params.range,
|
||||
params.sourceMap,
|
||||
params.timedRange,
|
||||
params.pairwiseTimedRange,
|
||||
params.onlySummary,
|
||||
params.runtimeTimerFilter,
|
||||
params.preprocessJson);
|
||||
}
|
||||
|
||||
constructor(
|
||||
cppEntriesProvider,
|
||||
separateIc,
|
||||
@ -588,7 +218,7 @@ export class TickProcessor extends LogReader {
|
||||
if (!sourceMap) return null;
|
||||
// Overwrite the load function to load scripts synchronously.
|
||||
WebInspector.SourceMap.load = (sourceMapURL) => {
|
||||
const content = d8.file.read(sourceMapURL);
|
||||
const content = this.readFile(sourceMapURL);
|
||||
const sourceMapObject = JSON.parse(content);
|
||||
return new SourceMap(sourceMapURL, sourceMapObject);
|
||||
};
|
||||
@ -963,3 +593,342 @@ export class TickProcessor extends LogReader {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CppEntriesProvider {
|
||||
inRange(funcInfo, start, end) {
|
||||
return funcInfo.start >= start && funcInfo.end <= end;
|
||||
}
|
||||
|
||||
parseVmSymbols(libName, libStart, libEnd, libASLRSlide, processorFunc) {
|
||||
this.loadSymbols(libName);
|
||||
|
||||
let lastUnknownSize;
|
||||
let lastAdded;
|
||||
|
||||
let addEntry = (funcInfo) => {
|
||||
// Several functions can be mapped onto the same address. To avoid
|
||||
// creating zero-sized entries, skip such duplicates.
|
||||
// Also double-check that function belongs to the library address space.
|
||||
|
||||
if (lastUnknownSize &&
|
||||
lastUnknownSize.start < funcInfo.start) {
|
||||
// Try to update lastUnknownSize based on new entries start position.
|
||||
lastUnknownSize.end = funcInfo.start;
|
||||
if ((!lastAdded ||
|
||||
!this.inRange(lastUnknownSize, lastAdded.start, lastAdded.end)) &&
|
||||
this.inRange(lastUnknownSize, libStart, libEnd)) {
|
||||
processorFunc(
|
||||
lastUnknownSize.name, lastUnknownSize.start, lastUnknownSize.end);
|
||||
lastAdded = lastUnknownSize;
|
||||
}
|
||||
}
|
||||
lastUnknownSize = undefined;
|
||||
|
||||
if (funcInfo.end) {
|
||||
// Skip duplicates that have the same start address as the last added.
|
||||
if ((!lastAdded || lastAdded.start != funcInfo.start) &&
|
||||
this.inRange(funcInfo, libStart, libEnd)) {
|
||||
processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
|
||||
lastAdded = funcInfo;
|
||||
}
|
||||
} else {
|
||||
// If a funcInfo doesn't have an end, try to match it up with then next
|
||||
// entry.
|
||||
lastUnknownSize = funcInfo;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const funcInfo = this.parseNextLine();
|
||||
if (funcInfo === null) continue;
|
||||
if (funcInfo === false) break;
|
||||
if (funcInfo.start < libStart - libASLRSlide &&
|
||||
funcInfo.start < libEnd - libStart) {
|
||||
funcInfo.start += libStart;
|
||||
} else {
|
||||
funcInfo.start += libASLRSlide;
|
||||
}
|
||||
if (funcInfo.size) {
|
||||
funcInfo.end = funcInfo.start + funcInfo.size;
|
||||
}
|
||||
addEntry(funcInfo);
|
||||
}
|
||||
addEntry({ name: '', start: libEnd });
|
||||
}
|
||||
|
||||
loadSymbols(libName) {}
|
||||
|
||||
parseNextLine() { return false }
|
||||
}
|
||||
|
||||
|
||||
export class UnixCppEntriesProvider extends CppEntriesProvider {
|
||||
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
|
||||
super();
|
||||
this.symbols = [];
|
||||
// File offset of a symbol minus the virtual address of a symbol found in
|
||||
// the symbol table.
|
||||
this.fileOffsetMinusVma = 0;
|
||||
this.parsePos = 0;
|
||||
this.nmExec = nmExec;
|
||||
this.objdumpExec = objdumpExec;
|
||||
this.targetRootFS = targetRootFS;
|
||||
this.apkEmbeddedLibrary = apkEmbeddedLibrary;
|
||||
this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
|
||||
}
|
||||
|
||||
|
||||
loadSymbols(libName) {
|
||||
this.parsePos = 0;
|
||||
if (this.apkEmbeddedLibrary && libName.endsWith('.apk')) {
|
||||
libName = this.apkEmbeddedLibrary;
|
||||
}
|
||||
if (this.targetRootFS) {
|
||||
libName = libName.substring(libName.lastIndexOf('/') + 1);
|
||||
libName = this.targetRootFS + libName;
|
||||
}
|
||||
try {
|
||||
this.symbols = [
|
||||
os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
|
||||
os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
|
||||
];
|
||||
|
||||
const objdumpOutput = os.system(this.objdumpExec, ['-h', libName], -1, -1);
|
||||
for (const line of objdumpOutput.split('\n')) {
|
||||
const [, sectionName, , vma, , fileOffset] = line.trim().split(/\s+/);
|
||||
if (sectionName === ".text") {
|
||||
this.fileOffsetMinusVma = parseInt(fileOffset, 16) - parseInt(vma, 16);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If the library cannot be found on this system let's not panic.
|
||||
this.symbols = ['', ''];
|
||||
}
|
||||
}
|
||||
|
||||
parseNextLine() {
|
||||
if (this.symbols.length == 0) {
|
||||
return false;
|
||||
}
|
||||
const lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
|
||||
if (lineEndPos == -1) {
|
||||
this.symbols.shift();
|
||||
this.parsePos = 0;
|
||||
return this.parseNextLine();
|
||||
}
|
||||
|
||||
const line = this.symbols[0].substring(this.parsePos, lineEndPos);
|
||||
this.parsePos = lineEndPos + 1;
|
||||
const fields = line.match(this.FUNC_RE);
|
||||
let funcInfo = null;
|
||||
if (fields) {
|
||||
funcInfo = { name: fields[3], start: parseInt(fields[1], 16) + this.fileOffsetMinusVma };
|
||||
if (fields[2]) {
|
||||
funcInfo.size = parseInt(fields[2], 16);
|
||||
}
|
||||
}
|
||||
return funcInfo;
|
||||
}
|
||||
}
|
||||
|
||||
export class MacCppEntriesProvider extends UnixCppEntriesProvider {
|
||||
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
|
||||
super(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary);
|
||||
// Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
|
||||
this.FUNC_RE = /^([0-9a-fA-F]{8,16})() (.*)$/;
|
||||
}
|
||||
|
||||
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), ''];
|
||||
} catch (e) {
|
||||
// If the library cannot be found on this system let's not panic.
|
||||
this.symbols = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class WindowsCppEntriesProvider extends CppEntriesProvider {
|
||||
constructor(_ignored_nmExec, _ignored_objdumpExec, targetRootFS,
|
||||
_ignored_apkEmbeddedLibrary) {
|
||||
super();
|
||||
this.targetRootFS = targetRootFS;
|
||||
this.symbols = '';
|
||||
this.parsePos = 0;
|
||||
}
|
||||
|
||||
static FILENAME_RE = /^(.*)\.([^.]+)$/;
|
||||
static FUNC_RE =
|
||||
/^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
|
||||
static IMAGE_BASE_RE =
|
||||
/^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
|
||||
// This is almost a constant on Windows.
|
||||
static EXE_IMAGE_BASE = 0x00400000;
|
||||
|
||||
loadSymbols(libName) {
|
||||
libName = this.targetRootFS + libName;
|
||||
const fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
|
||||
if (!fileNameFields) return;
|
||||
const mapFileName = `${fileNameFields[1]}.map`;
|
||||
this.moduleType_ = fileNameFields[2].toLowerCase();
|
||||
try {
|
||||
this.symbols = read(mapFileName);
|
||||
} catch (e) {
|
||||
// If .map file cannot be found let's not panic.
|
||||
this.symbols = '';
|
||||
}
|
||||
}
|
||||
|
||||
parseNextLine() {
|
||||
const lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
|
||||
if (lineEndPos == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const line = this.symbols.substring(this.parsePos, lineEndPos);
|
||||
this.parsePos = lineEndPos + 2;
|
||||
|
||||
// Image base entry is above all other symbols, so we can just
|
||||
// terminate parsing.
|
||||
const imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
|
||||
if (imageBaseFields) {
|
||||
const imageBase = parseInt(imageBaseFields[1], 16);
|
||||
if ((this.moduleType_ == 'exe') !=
|
||||
(imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
|
||||
return fields ?
|
||||
{ name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs very simple unmangling of C++ names.
|
||||
*
|
||||
* Does not handle arguments and template arguments. The mangled names have
|
||||
* the form:
|
||||
*
|
||||
* ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
|
||||
*/
|
||||
unmangleName(name) {
|
||||
// Empty or non-mangled name.
|
||||
if (name.length < 1 || name.charAt(0) != '?') return name;
|
||||
const nameEndPos = name.indexOf('@@');
|
||||
const components = name.substring(1, nameEndPos).split('@');
|
||||
components.reverse();
|
||||
return components.join('::');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ArgumentsProcessor extends BaseArgumentsProcessor {
|
||||
getArgsDispatch() {
|
||||
let dispatch = {
|
||||
__proto__:null,
|
||||
'-j': ['stateFilter', TickProcessor.VmStates.JS,
|
||||
'Show only ticks from JS VM state'],
|
||||
'-g': ['stateFilter', TickProcessor.VmStates.GC,
|
||||
'Show only ticks from GC VM state'],
|
||||
'-p': ['stateFilter', TickProcessor.VmStates.PARSER,
|
||||
'Show only ticks from PARSER VM state'],
|
||||
'-b': ['stateFilter', TickProcessor.VmStates.BYTECODE_COMPILER,
|
||||
'Show only ticks from BYTECODE_COMPILER VM state'],
|
||||
'-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
|
||||
'Show only ticks from COMPILER VM state'],
|
||||
'-o': ['stateFilter', TickProcessor.VmStates.OTHER,
|
||||
'Show only ticks from OTHER VM state'],
|
||||
'-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
|
||||
'Show only ticks from EXTERNAL VM state'],
|
||||
'--filter-runtime-timer': ['runtimeTimerFilter', null,
|
||||
'Show only ticks matching the given runtime timer scope'],
|
||||
'--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
|
||||
'Set the call graph size'],
|
||||
'--ignore-unknown': ['ignoreUnknown', true,
|
||||
'Exclude ticks of unknown code entries from processing'],
|
||||
'--separate-ic': ['separateIc', parseBool,
|
||||
'Separate IC entries'],
|
||||
'--separate-bytecodes': ['separateBytecodes', parseBool,
|
||||
'Separate Bytecode entries'],
|
||||
'--separate-builtins': ['separateBuiltins', parseBool,
|
||||
'Separate Builtin entries'],
|
||||
'--separate-stubs': ['separateStubs', parseBool,
|
||||
'Separate Stub entries'],
|
||||
'--separate-baseline-handlers': ['separateBaselineHandlers', parseBool,
|
||||
'Separate Baseline Handler entries'],
|
||||
'--unix': ['platform', 'unix',
|
||||
'Specify that we are running on *nix platform'],
|
||||
'--windows': ['platform', 'windows',
|
||||
'Specify that we are running on Windows platform'],
|
||||
'--mac': ['platform', 'mac',
|
||||
'Specify that we are running on Mac OS X platform'],
|
||||
'--nm': ['nm', 'nm',
|
||||
'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
|
||||
'--objdump': ['objdump', 'objdump',
|
||||
'Specify the \'objdump\' executable to use (e.g. --objdump=/my_dir/objdump)'],
|
||||
'--target': ['targetRootFS', '',
|
||||
'Specify the target root directory for cross environment'],
|
||||
'--apk-embedded-library': ['apkEmbeddedLibrary', '',
|
||||
'Specify the path of the embedded library for Android traces'],
|
||||
'--range': ['range', 'auto,auto',
|
||||
'Specify the range limit as [start],[end]'],
|
||||
'--distortion': ['distortion', 0,
|
||||
'Specify the logging overhead in picoseconds'],
|
||||
'--source-map': ['sourceMap', null,
|
||||
'Specify the source map that should be used for output'],
|
||||
'--timed-range': ['timedRange', true,
|
||||
'Ignore ticks before first and after last Date.now() call'],
|
||||
'--pairwise-timed-range': ['pairwiseTimedRange', true,
|
||||
'Ignore ticks outside pairs of Date.now() calls'],
|
||||
'--only-summary': ['onlySummary', true,
|
||||
'Print only tick summary, exclude other information'],
|
||||
'--serialize-vm-symbols': ['serializeVMSymbols', true,
|
||||
'Print all C++ symbols and library addresses as JSON data'],
|
||||
'--preprocess': ['preprocessJson', true,
|
||||
'Preprocess for consumption with web interface']
|
||||
};
|
||||
dispatch['--js'] = dispatch['-j'];
|
||||
dispatch['--gc'] = dispatch['-g'];
|
||||
dispatch['--compiler'] = dispatch['-c'];
|
||||
dispatch['--other'] = dispatch['-o'];
|
||||
dispatch['--external'] = dispatch['-e'];
|
||||
dispatch['--ptr'] = dispatch['--pairwise-timed-range'];
|
||||
return dispatch;
|
||||
}
|
||||
|
||||
getDefaultResults() {
|
||||
return {
|
||||
logFileName: 'v8.log',
|
||||
platform: 'unix',
|
||||
stateFilter: null,
|
||||
callGraphSize: 5,
|
||||
ignoreUnknown: false,
|
||||
separateIc: true,
|
||||
separateBytecodes: false,
|
||||
separateBuiltins: true,
|
||||
separateStubs: true,
|
||||
separateBaselineHandlers: false,
|
||||
preprocessJson: null,
|
||||
sourceMap: null,
|
||||
targetRootFS: '',
|
||||
nm: 'nm',
|
||||
objdump: 'objdump',
|
||||
range: 'auto,auto',
|
||||
distortion: 0,
|
||||
timedRange: false,
|
||||
pairwiseTimedRange: false,
|
||||
onlySummary: false,
|
||||
runtimeTimerFilter: null,
|
||||
serializeVMSymbols: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user