text: (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]: "<total>", [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));
      },
    };
  }());
  