// 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: , total: } // func_name2: {name: func_name1, count: , total: } // ... // } WebAssembly.getProfile = (instance) => { return instanceMap.get(instance); } })();