Commit 4eb53245 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[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: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57746}
parent 4fca7b00
// 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();
// 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);
}
})();
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