[wasm/tools] Add import profiler
Add a profiler for functions imported to WASM instances. This profiler is implemented entirely in JavaScript and monkey-patches WebAssembly.instantiate() and new WebAssembly.Instance() to instrument the imported functions to each instance in order to count their invocations and cumulative time. R=mstarzinger@chromium.org Bug: v8:8423 Change-Id: If456355aba07dc69c5500bafbe35fc56b31486af Reviewed-on: https://chromium-review.googlesource.com/c/1347488 Commit-Queue: Ben Titzer <titzer@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#57746}
This commit is contained in:
parent
4fca7b00e8
commit
4eb53245f0
6
tools/wasm/wasm-import-profiler-end.js
Normal file
6
tools/wasm/wasm-import-profiler-end.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Code to run at shutdown: print out the profiles for all instances.
|
||||
if (typeof WebAssembly.dumpAllProfiles == "function") WebAssembly.dumpAllProfiles();
|
131
tools/wasm/wasm-import-profiler.js
Normal file
131
tools/wasm/wasm-import-profiler.js
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
(() => {
|
||||
let all_profiles = [];
|
||||
let instanceMap = new WeakMap();
|
||||
let instanceCounter = 0;
|
||||
|
||||
function instrument(imports, profile) {
|
||||
let orig_imports = imports;
|
||||
return new Proxy(imports, {
|
||||
get: (obj, module_name) => {
|
||||
let orig_module = orig_imports[module_name];
|
||||
return new Proxy(orig_module, {
|
||||
get: (obj, item_name) => {
|
||||
let orig_func = orig_module[item_name];
|
||||
let item = orig_func;
|
||||
if (typeof orig_func == "function") {
|
||||
var full_name = module_name + "." + item_name;
|
||||
print("instrumented " + full_name);
|
||||
profile[full_name] = {name: full_name, count: 0, total: 0};
|
||||
item = function profiled_func(...args) {
|
||||
var before = performance.now();
|
||||
var result = orig_func(...args);
|
||||
var delta = performance.now() - before;
|
||||
var data = profile[full_name];
|
||||
data.count++;
|
||||
data.total += delta;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dumpProfile(profile) {
|
||||
let array = [];
|
||||
for (let key in profile) {
|
||||
if (key == "instanceNum") continue;
|
||||
let data = profile[key];
|
||||
if (data.count == 0) continue;
|
||||
array.push(data);
|
||||
}
|
||||
print(`--- Import profile for instance ${profile.instanceNum} ---`);
|
||||
if (array.length == 0) return;
|
||||
array.sort((a, b) => b.total - a.total);
|
||||
for (let data of array) {
|
||||
print(`${padl(data.name, 30)}: ${padr(data.count, 10)} ${padp(data.total, 10)}ms`);
|
||||
}
|
||||
}
|
||||
|
||||
function padl(s, len) {
|
||||
s = s.toString();
|
||||
while (s.length < len) s = s + " ";
|
||||
return s;
|
||||
}
|
||||
function padr(s, len) {
|
||||
s = s.toString();
|
||||
while (s.length < len) s = " " + s;
|
||||
return s;
|
||||
}
|
||||
function padp(s, len) {
|
||||
s = s.toString();
|
||||
var i = s.indexOf(".");
|
||||
if (i == -1) i = s.length;
|
||||
while (i++ < len) s = " " + s;
|
||||
return s;
|
||||
}
|
||||
|
||||
// patch: WebAssembly.instantiate (async)
|
||||
let orig_instantiate = WebAssembly.instantiate;
|
||||
WebAssembly.instantiate = (m, imports, ...args) => {
|
||||
let profile = {};
|
||||
let promise = orig_instantiate(m, instrument(imports, profile), ...args);
|
||||
promise.then((instance) => {
|
||||
instanceMap.set(instance, profile);
|
||||
all_profiles.push(profile);
|
||||
profile.instanceNum = instanceCounter++;
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
// patch: new WebAssembly.Instance (sync)
|
||||
let orig_new_instance = WebAssembly.Instance;
|
||||
WebAssembly.Instance = new Proxy(orig_new_instance, {
|
||||
construct: (target, args) => {
|
||||
let profile = {};
|
||||
args[1] = instrument(args[1], profile);
|
||||
let instance = new orig_new_instance(...args);
|
||||
instanceMap.set(instance, profile);
|
||||
all_profiles.push(profile);
|
||||
profile.instanceNum = instanceCounter++;
|
||||
return instance;
|
||||
}
|
||||
});
|
||||
|
||||
// expose: WebAssembly.dumpProfile(instance)
|
||||
WebAssembly.dumpProfile = (instance) => {
|
||||
let profile = instanceMap.get(instance);
|
||||
if (profile === undefined) return;
|
||||
dumpProfile(profile);
|
||||
}
|
||||
// expose: WebAssembly.clearProfile(instance)
|
||||
WebAssembly.clearProfile = (instance) => {
|
||||
let profile = instanceMap.get(instance);
|
||||
if (profile === undefined) return;
|
||||
for (let key in profile) {
|
||||
if (key == "instanceNum") continue;
|
||||
let data = p[key];
|
||||
data.count = 0;
|
||||
data.total = 0;
|
||||
}
|
||||
}
|
||||
// expose: WebAssembly.dumpAllProfiles()
|
||||
WebAssembly.dumpAllProfiles = () => {
|
||||
for (let profile of all_profiles) dumpProfile(profile);
|
||||
}
|
||||
// expose: WebAssembly.getProfile(instance)
|
||||
// returns: {
|
||||
// func_name1: {name: func_name1, count: <num>, total: <num>}
|
||||
// func_name2: {name: func_name1, count: <num>, total: <num>}
|
||||
// ...
|
||||
// }
|
||||
WebAssembly.getProfile = (instance) => {
|
||||
return instanceMap.get(instance);
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue
Block a user