(function () { class Allocation { constructor(size, align, pointer) { this.size = size; this.align = align; this.pointer = pointer; this.stack = getStack(); } } class InvalidFree { constructor(size, align, pointer) { this.size = size; this.align = align; this.pointer = pointer; this.stack = getStack(); } } const liveAllocs = new Map(); const invalidFrees = new Map(); function getStack() { return Error() .stack .split("\n") .filter(frame => frame.match(/hooks\.js/) === null) .join("\n"); } function onAlloc(size, align, pointer) { liveAllocs.set(pointer, new Allocation(size, align, pointer)); } function onDealloc(size, align, pointer) { const wasLive = liveAllocs.delete(pointer); if (!wasLive) { invalidFrees.push(new InvalidFree(size, align, pointer)); } } function onAllocZeroed(size, align, pointer) { onAlloc(size, align, pointer); } function onRealloc( oldPointer, newPointer, oldSize, newSize, align, ) { onDealloc(oldSize, align, oldPointer); onAlloc(newSize, align, newPointer); } function dumpTable(entries, { keyLabel, valueLabel, getKey, getValue }) { const byKey = new Map; let total = 0; for (const entry of entries) { const key = getKey(entry); const keyValue = byKey.get(key) || 0; const entryValue = getValue(entry); total += entryValue; byKey.set(key, keyValue + entryValue); } const table = [...byKey] .sort((a, b) => b[1] - a[1]) .map(a => ({ [keyLabel]: a[0], [valueLabel]: a[1] })); table.unshift({ [keyLabel]: "", [valueLabel]: total }); console.table(table, [keyLabel, valueLabel]); } function getGlobal() { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); } getGlobal().WasmTracingAllocator = { on_alloc: onAlloc, on_dealloc: onDealloc, on_alloc_zeroed: onAllocZeroed, on_realloc: onRealloc, dumpLiveAllocations(opts) { dumpTable(liveAllocs.values(), Object.assign({ keyLabel: "Live Allocations", valueLabel: "Size (Bytes)", getKey: entry => entry.stack, getValue: _entry => 1, }, opts)); }, dumpInvalidFrees(opts) { dumpTable(invalidFrees, Object.assign({ keyLabel: "Invalid Free", valueLabel: "Count", getKey: entry => entry.stack, getValue: _entry => 1, }, opts)); }, }; }());