├── .gitignore ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | DEADJOE 3 | .#* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | require-timer 2 | ---------- 3 | 4 | Track and report module load times in Node.js 5 | 6 | Synopsis 7 | -------- 8 | 9 | require('require-timer'); // output load timing information to stderr 10 | 11 | require('require-timer')(process.stdout); // output load timing information to stdout 12 | 13 | Description 14 | ----------- 15 | 16 | This is a very simple module that instruments module loading to track how 17 | long it takes to require your modules. Intended to give data to help people 18 | improve the startup times of their command line tools. 19 | 20 | Loading this module installs it globally, causing it to track load times 21 | across ALL calls to `require`. Obviously, if you've written your own module 22 | loader it won't instrument that. 23 | 24 | Obviously it only instruments load times *after* its been loaded, so 25 | typically the first thing you do should be to load it. 26 | 27 | Output occurs when the program exits. 28 | 29 | Output 30 | ------ 31 | 32 | An example of the first few lines of running this on npm: 33 | 34 | ```` 35 | 295.038 msec from start, 1.542 msec to load: cli.js 36 | 1.322 msec from start, 1.322 msec to load: cli.js -> require-timer 37 | 294.891 msec from start, 35.301 msec to load: cli.js -> bin/npm-cli.js 38 | 35.164 msec from start, 3.728 msec to load: cli.js -> bin/npm-cli.js -> npmlog 39 | 34.485 msec from start, 1.189 msec to load: cli.js -> bin/npm-cli.js -> npmlog -> ansi 40 | 34.131 msec from start, 0.205 msec to load: cli.js -> bin/npm-cli.js -> npmlog -> ansi -> lib/newlines.js 41 | 44.787 msec from start, 1.529 msec to load: cli.js -> bin/npm-cli.js -> graceful-fs 42 | 42.721 msec from start, 3.842 msec to load: cli.js -> bin/npm-cli.js -> graceful-fs -> fs.js 43 | 44.564 msec from start, 1.278 msec to load: cli.js -> bin/npm-cli.js -> graceful-fs -> polyfills.js 44 | 45 | ```` 46 | 47 | The output is ordered by when the module began being loaded. That is, the 48 | output is in *source code* order. 49 | 50 | The first number, is the amount of time into execution that this module COMPLETED loading. 51 | 52 | The second number is how long the module took to load, not including the modules it loaded. 53 | 54 | Finally, have the require stack: 55 | 56 | * Modules show up as just a module name (Note that they may have been loaded 57 | from a node_modules anywhere in your search path, this only shows you who 58 | asked for it, not where it was stored.) 59 | 60 | * Regular files show up as relative to the requiring module's root, so for 61 | example, when you see: `graceful-fs -> fs.js` this means that you'll find: 62 | `node_modules/graceful-fs/fs.js`. It's still the *module's root* even 63 | when it's a file doing the including, so for example, 64 | `npmconf -> lib/load-prefix.js -> lib/find-prefix.js`, load-prefix and 65 | find-prefix are in the same folder. 66 | 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module._requireTimer = {start: time()}; 3 | var path = require('path'); 4 | module._requireTimer.name = 'require-timer'; 5 | module._requireTimer.path = path.dirname(module.filename); 6 | 7 | var out = process.stderr; 8 | 9 | module.exports = function (stream) { 10 | out = stream; 11 | } 12 | 13 | function time() { 14 | var hrtime = process.hrtime(); 15 | return hrtime[0] * 1e3 + hrtime[1] / 1e6; // 16 | } 17 | 18 | function escapeRegExp(string){ 19 | return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1"); 20 | } 21 | 22 | function findTop(module) { return module.parent ? findTop(module.parent) : module } 23 | var top = findTop(module); 24 | top._requireTimer = {name: path.basename(top.filename), path: path.dirname(top.filename), start: module._requireTimer.start}; 25 | 26 | var resafesep = escapeRegExp(path.sep); 27 | var modmatch = new RegExp('^node_modules'+resafesep+'([^'+resafesep+']+)'); 28 | 29 | var loading = top; 30 | 31 | var defaultLoader = require.extensions['.js']; 32 | require.extensions['.js'] = function (module) { 33 | if (module.loaded) { 34 | return defaultLoader.apply(null,arguments); 35 | } 36 | // module.parent != the module being required that triggered this to be required. 37 | // Rather, module.parent is the 38 | if (loading && loading !== module.parent) { 39 | module.parent.children = module.parent.children.filter(function(M){ return M !== module }); 40 | loading.children.push(module); 41 | module.parent = loading; 42 | } 43 | var current = module._requireTimer = { 44 | name: null, 45 | path: null, 46 | start: null, 47 | end: null 48 | }; 49 | var matched = false; 50 | var parent = module; 51 | while (parent = parent.parent) { 52 | var relpath = path.relative(parent._requireTimer.path,module.filename); 53 | if (relpath[0] != '.') { 54 | current.name = relpath; 55 | matched = true; 56 | break; 57 | } 58 | } 59 | 60 | if (!matched) { 61 | current.path = module.parent._requireTimer.path; 62 | current.name = path.relative(current.path,module.filename); 63 | } 64 | else { 65 | var matches; 66 | if (0 === current.name.indexOf('node_modules'+path.sep)) { 67 | var namechunk = current.name.substr(12+path.sep.length); 68 | var namelength = namechunk.indexOf(path.sep); 69 | current.name = namechunk.substr(0,namelength); 70 | var moduleprefix = 'node_modules'+path.sep+current.name+path.sep; 71 | var module_path_length = module.filename.lastIndexOf(moduleprefix) + moduleprefix.length; 72 | current.path = module.filename.substr(0,module_path_length); 73 | } 74 | else { 75 | current.path = parent._requireTimer.path; 76 | } 77 | } 78 | var previous = loading; 79 | loading = module; 80 | current.start = time(); 81 | var result = defaultLoader.apply(null,arguments); 82 | current.end = time(); 83 | loading = previous; 84 | 85 | return result; 86 | } 87 | 88 | process.nextTick(function () { 89 | top._requireTimer.end = time(); 90 | // we make a fake module here to track any loads that occur after the 91 | // main function exits but before the program exits. 92 | loading = {children:[], parent:null, _requireTimer:{path:top._requireTimer.path,name:'async',start:time(),end:time()}}; 93 | }); 94 | process.on('exit', function(code) { 95 | if (!top._requireTimer.end) { 96 | top._requireTimer.end = time(); 97 | loading = null; 98 | } 99 | require.extensions['.js'] = defaultLoader; 100 | var sprintf = require('sprintf'); 101 | var startupreport = timingReport(top).results; 102 | var report; 103 | if (loading) { 104 | var asyncreport = timingReport(loading).results; 105 | report = startupreport.concat(asyncreport); 106 | } 107 | else { 108 | report = startupreport; 109 | } 110 | report.sort(function(A,B){ 111 | return A.start > B.start ? 1 : A.start < B.start ? -1 : A.stack > B.stack ? 1 : A.stack < B.stack ? -1 : 0 112 | }).forEach(function(R) { 113 | out.write(sprintf('%9.3f msec from start, %9.3f msec to load: %s\n', R.time, R.load<0?0:R.load, R.stack)); 114 | }); 115 | }); 116 | 117 | function timingReport(module,stack) { 118 | var results = []; 119 | var childAtLoad = 0; 120 | stack = stack ? stack + ' -> '+module._requireTimer.name : module._requireTimer.name; 121 | for (var ii=0; ii (http://re-becca.org/)", 22 | "license": "BSD", 23 | "bugs": { 24 | "url": "https://github.com/iarna/require-timer/issues" 25 | }, 26 | "homepage": "https://github.com/iarna/require-timer", 27 | "dependencies": { 28 | "sprintf": "^0.1.3" 29 | } 30 | } 31 | --------------------------------------------------------------------------------