docs: function infect(item, i, modulePath) {
    var original = item[i];
    if (original.__infected) return;
    item[i] = function() {
      return (function() {
        var functionArgs = Array.prototype.slice.call(arguments);
        // If we're not tracing, don't do anything.
        if (!active) return original.apply(this, functionArgs);
  
        // Start gathering stats for when the tracing is stopped
        stats[modulePath] = stats[modulePath] || { totalTime: 0, count: 0, min: 99999, max: 0, calcs: 1 };
        stats[modulePath].count++;
  
        // These are the timings we're interested in
        var functionInvokedAt = meaningfulTime();
        var functionReturnedAt = null;
        var callbackInvokedAt = null;
        var callbackReturnedAt = null;
        var isAsync = false;
        var indentationWhenInvoked = currentIndentation;
        var spaces = space.substring(0, currentIndentation);
  
        // If the tracer function was invoked with any other functions, infect those too
        // (this captures a lot of anonymous, otherwise hidden, functions)
        functionArgs = functionArgs.map(function(arg) {
          if (!(arg instanceof Function)) return arg;
          var index = '['+functionArgs.indexOf(arg)+']';
          // This covers functions passed in which are NOT the last argument
          if (functionArgs.indexOf(arg) != (functionArgs.length-1)) {
            return function() {
              currentIndentation = indentationWhenInvoked;
              console.error('TRACER CBI', meaningfulTime(), processRef.memoryUsage().heapUsed, spaces, modulePath+index);
              var out = arg.apply(this, Array.prototype.slice.call(arguments));
              if (!active) return;
              console.error('TRACER CBR', meaningfulTime(), processRef.memoryUsage().heapUsed, spaces, modulePath+index);
              stats[modulePath].calcs += 2;
              return out;
            };
          }
          // If it gets here, we're dealing with the final argument and it's a function (read: the callback)
          isAsync = true;
          return function() {
            currentIndentation = indentationWhenInvoked;
  
            callbackInvokedAt = meaningfulTime();
            console.error('TRACER CBI', callbackInvokedAt, processRef.memoryUsage().heapUsed, spaces, modulePath+index);
  
            var out = arg.apply(this, Array.prototype.slice.call(arguments));
            if (!active) return;
  
            callbackReturnedAt = meaningfulTime();
            console.error('TRACER CBR', callbackReturnedAt, processRef.memoryUsage().heapUsed, spaces, modulePath+index);
  
            var callbackDuration = timeDiff(callbackReturnedAt, callbackInvokedAt);
            if (functionReturnedAt) {
              stats[modulePath].totalTime += timeDiff(callbackInvokedAt, functionReturnedAt);
              stats[modulePath].totalTime += callbackDuration;
            }
            stats[modulePath].calcs += 2;
  
            var totalDuration = timeDiff(callbackReturnedAt, functionInvokedAt);
            if (totalDuration > stats[modulePath].max) stats[modulePath].max = totalDuration;
            if (totalDuration < stats[modulePath].min) stats[modulePath].min = totalDuration;
  
            return out;
          };
        });
  
        console.error('TRACER FNI', functionInvokedAt, processRef.memoryUsage().heapUsed, spaces, modulePath);
  
        currentIndentation += 2;
        var out = original.apply(this, functionArgs);
        if (!active) return;
        currentIndentation -= 2;
  
        functionReturnedAt = meaningfulTime();
        console.error('TRACER FNR', functionReturnedAt, processRef.memoryUsage().heapUsed, spaces, modulePath);
  
        // Guess how long it took
        var functionDuration = timeDiff(functionReturnedAt, functionInvokedAt);
        stats[modulePath].totalTime += functionDuration;
        stats[modulePath].calcs++;
        if (functionDuration > stats[modulePath].max) stats[modulePath].max = functionDuration;
        if (!isAsync) {
          if (functionDuration < stats[modulePath].min) stats[modulePath].min = functionDuration;
        }
  
        // Our new function still returns the value of the original
        return out;
      }).apply(this, Array.prototype.slice.call(arguments));
    };
  
    // This bit examines the params required by the original function and copies them into our new function.
    // It's used to allow dependency injection or Function.toString() to work
    var dependencies = original.toString().match(/^function .*?\((.*?)\)/);
    if (dependencies) {
      var newFunc = item[i].toString();
      newFunc = '(function() { return '+newFunc.replace('function ()', 'function ('+dependencies[1]+')')+ '; })()';
      try {
        item[i] = eval(newFunc);
      } catch(e) { }
    }
  
    // Make sure we don't lose any prototypes!
    item[i].prototype = original.prototype;
  
    // Tag the new function so we don't trace it twice
    item[i].__infected = true;
  
    allFuncs.push(item[i]);
  }