├── .gitignore ├── .travis.yml ├── LICENCE ├── benchmark ├── index.js ├── server.js ├── traced-contexts-server.js ├── traced-server.js └── traced-stacks-server.js ├── example.js ├── index.js ├── package.json ├── readme.md ├── smoke.js ├── test ├── index.js └── test.js └── wrappers.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage 15 | .nyc-output 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | profile-* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "4" 5 | - "5.9.1" 6 | - "5" 7 | - "6" 8 | env: 9 | - CC=gcc-4.8 CXX=g++-4.8 10 | addons: 11 | apt: 12 | sources: 13 | - ubuntu-toolchain-r-test 14 | packages: 15 | - gcc-4.8 16 | - g++-4.8 17 | - zlib1g-dev -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 David Mark Clements 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | var spawn = require('child_process').spawn 2 | var path = require('path') 3 | var autocannon = path.join(__dirname, '..', 'node_modules', '.bin', 'autocannon') 4 | var flags = [ 5 | '-d', '10', '-p', '10', 'http://localhost:3000' 6 | ] 7 | var opts = {stdio: 'inherit'} 8 | 9 | var queue = (~process.argv.indexOf('--opts-compare')) 10 | ? [bench('traced-contexts', 'traced-contexts-server'), bench('traced-stacks', 'traced-stacks-server'), bench('traced', 'traced-server')] 11 | : [bench('traced', 'traced-server'), bench('control', 'server')] 12 | 13 | queue.shift()() 14 | 15 | function bench (name, file) { 16 | return function () { 17 | console.log('Profiling ' + name + ' server for 10 seconds') 18 | var server = spawn('node', [path.join(__dirname, file)]) 19 | spawn(autocannon, flags, opts) 20 | .on('exit', function () { 21 | server.kill() 22 | server.on('exit', function () { 23 | var next = queue.shift() 24 | if (next) next() 25 | }) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /benchmark/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var http = require('http') 4 | var server = http.createServer(handle) 5 | 6 | function handle (req, res) { 7 | res.end('hello world') 8 | } 9 | 10 | server.listen(3000) 11 | -------------------------------------------------------------------------------- /benchmark/traced-contexts-server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var http = require('http') 4 | require('../')(process.stdout, {contexts: true}) 5 | var server = http.createServer(handle) 6 | 7 | function handle (req, res) { 8 | res.end('hello world') 9 | } 10 | 11 | server.listen(3000) 12 | -------------------------------------------------------------------------------- /benchmark/traced-server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var http = require('http') 4 | require('../')(process.stdout) 5 | var server = http.createServer(handle) 6 | 7 | function handle (req, res) { 8 | res.end('hello world') 9 | } 10 | 11 | server.listen(3000) 12 | -------------------------------------------------------------------------------- /benchmark/traced-stacks-server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var http = require('http') 4 | require('../')(process.stdout, {stacks: true}) 5 | var server = http.createServer(handle) 6 | 7 | function handle (req, res) { 8 | res.end('hello world') 9 | } 10 | 11 | server.listen(3000) 12 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | require('./')() 3 | 4 | http.createServer(function (req, res) { 5 | res.end('hello world') 6 | }).listen(3000) 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var wrap = process.binding('async_wrap') 3 | var stringify = require('json-stringify-safe') 4 | var mappings = Object.keys(wrap.Providers) 5 | .reduce(function (o, k) { 6 | o[wrap.Providers[k]] = k.replace('WRAP', '') 7 | return o 8 | }, {}) 9 | var flatstr = require('flatstr') 10 | var areaMap = [ 11 | 'misc', 'crypto', 'fs', 'fs', 'dns', 'dns', 'http', 'stream', 'stream', 'stream', 12 | 'process', 'dns', 'stream', 'process', 'fs', 'net', 'net', 'timers', 'tls', 'tty', 13 | 'dgram', 'dgram', 'stream', 'zlib' 14 | ] 15 | 16 | module.exports = function (f, opts) { 17 | var prepareStackTrace = Error.prepareStackTrace 18 | var stackTraceLimit = Error.stackTraceLimit 19 | 20 | if (typeof f === 'object') { 21 | if (!f.pipe || !f.cork) { 22 | opts = f 23 | f = null 24 | } 25 | } 26 | 27 | opts = opts || {} 28 | var legacy = false 29 | var autostart = opts.autostart 30 | var append = opts.append 31 | var stacks = opts.stacks 32 | var suffix = opts.suffix 33 | var prefix = opts.prefix 34 | var contexts = opts.contexts 35 | 36 | f = f || 1 37 | 38 | if (typeof autostart === 'undefined') { 39 | autostart = true 40 | } 41 | 42 | if (typeof prefix === 'object') { 43 | prefix = JSON.stringify(opts.prefix) 44 | prefix = prefix.substring(1, prefix.length - 1) + ',' 45 | } else { 46 | prefix = '' 47 | } 48 | 49 | if (typeof suffix === 'object') { 50 | suffix = JSON.stringify(opts.suffix) 51 | suffix = ',' + suffix.substring(1, suffix.length - 1) 52 | } else { 53 | suffix = '' 54 | } 55 | 56 | if (typeof f === 'string') { 57 | f = fs.openSync(f, append ? 'a' : 'w') 58 | } 59 | var ops = new Map() 60 | var enable = function () { wrap.enable() } 61 | var disable = function () { wrap.disable() } 62 | disable() 63 | try { 64 | wrap.setupHooks({init: init, pre: pre, post: post, destroy: destroy}) 65 | } catch (e) { 66 | // covers 5.9.1 down to 5, 4 and below is legacy 67 | legacy = (process.version[1] !== '5') 68 | wrap.setupHooks(init, pre, post, destroy) 69 | } 70 | 71 | if (autostart) { enable() } 72 | 73 | function write (s) { 74 | process.nextTick(function (s) { 75 | disable() 76 | if (f.pipe && f.cork) { 77 | f.write(flatstr(s), enable) 78 | } else { 79 | fs.write(f, flatstr(s), enable) 80 | } 81 | }, s) 82 | } 83 | 84 | return { 85 | enable: enable, 86 | disable: disable 87 | } 88 | 89 | function init (uid, provider, parentUid, parentHandle) { 90 | if (legacy) { 91 | Object.defineProperty(this, '_legacy_uid', {value: provider}) 92 | provider = uid 93 | uid = this._legacy_uid 94 | } 95 | var op = mappings[provider] 96 | var area = areaMap[provider] 97 | var ctx = contexts ? this : null 98 | ops.set(uid, {op: op, ctx: ctx, area: area}) 99 | var parent = parentHandle && 100 | parentHandle.constructor.name.toUpperCase().replace('WRAP', '') 101 | var s = stacks 102 | write('{' + prefix + '"opid":' + uid + ',"op":"' + op + '",' + '"phase":"init","area":"' + area + '"' + (parent ? ',"parentopid":' + parentUid + ',"parentop":"' + parent + '"' : '') + ',"time":' + Date.now() + (s ? ',"stack":' + stack() : '') + suffix + '}\n') 103 | } 104 | function pre (uid) { 105 | uid = uid || this._legacy_uid 106 | var state = ops.get(uid) 107 | var ctx = state.ctx 108 | write('{' + prefix + '"opid":' + uid + ',"op":"' + state.op + '","phase":"pre","area":"' + state.area + '","time":' + Date.now() + (ctx ? ',"ctx":' + stringify(ctx) : '') + suffix + '}\n') 109 | } 110 | function post (uid, threw) { 111 | uid = uid || this._legacy_uid 112 | var state = ops.get(uid) 113 | var ctx = state.ctx 114 | write('{' + prefix + '"opid":' + uid + ',"op":"' + state.op + '","phase":"post","area":"' + state.area + '"' + (threw ? ',"threw":' + threw : '') + ',"time":' + Date.now() + (ctx ? ',"ctx":' + stringify(ctx) : '') + suffix + '}\n') 115 | } 116 | function destroy (uid) { 117 | var state = ops.get(uid) 118 | write('{' + prefix + '"opid":' + uid + ',"op":"' + state.op + '","phase":"destroy","area":"' + state.area + '","time":' + Date.now() + suffix + '}\n') 119 | ops.delete(uid) 120 | } 121 | 122 | function stack () { 123 | Error.prepareStackTrace = function (s, frames) { 124 | var stacks = '[' 125 | var f 126 | var i = 3 127 | var len = frames.length 128 | 129 | for (i; i < len; i++) { 130 | f = frames[i] 131 | stacks += '"' + (f.getFunctionName() || f.getMethodName() || '') + ':' + 132 | f.getFileName() + ':' + f.getLineNumber() + ':' + f.getColumnNumber() + '"' + 133 | (i === len - 1 ? '' : ',') 134 | } 135 | 136 | return stacks + ']' 137 | } 138 | Error.stackTraceLimit = typeof stacks === 'number' ? stacks + 3 : Infinity 139 | var s = Error().stack 140 | Error.prepareStackTrace = prepareStackTrace 141 | Error.stackTraceLimit = stackTraceLimit 142 | return s 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-tracer", 3 | "version": "0.6.1", 4 | "description": "Trace all async operations, output as newline delimited JSON logs, with minimal overhead.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tap test/index.js", 8 | "benchmark": "node benchmark", 9 | "benchmark-options": "node benchmark --opts-compare" 10 | }, 11 | "author": "David Mark Clements", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "autocannon": "^0.4.0", 15 | "standard": "^7.0.1", 16 | "tap": "^5.7.1" 17 | }, 18 | "directories": { 19 | "test": "test" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/davidmarkclements/async-tracer.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/davidmarkclements/async-tracer/issues" 27 | }, 28 | "homepage": "https://github.com/davidmarkclements/async-tracer#readme", 29 | "dependencies": { 30 | "fast-safe-stringify": "^1.0.9", 31 | "flatstr": "^1.0.3", 32 | "json-stringify-safe": "^5.0.1", 33 | "quick-format": "^2.0.4", 34 | "reusify": "^1.0.1" 35 | }, 36 | "engines": { 37 | "node": ">=4 <8" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # async-tracer 2 | 3 | Trace all async operations, output as newline delimited JSON logs, 4 | with minimal overhead. 5 | 6 | [![Build Status](https://travis-ci.org/davidmarkclements/async-tracer.svg)](https://travis-ci.org/davidmarkclements/async-tracer) 7 | 8 | ## Supports 9 | 10 | Node v4 to v6 11 | 12 | ## Usage 13 | 14 | ### Write to a stream 15 | 16 | ```js 17 | require('async-tracer')(process.stderr) 18 | ``` 19 | 20 | ### Write to a path 21 | 22 | ```js 23 | require('async-tracer')('/var/async.' + process.pid + '.log') 24 | ``` 25 | 26 | ### Write to a file handle 27 | 28 | ```js 29 | require('async-tracer')(1) // 1 is STDOUT, default 30 | require('async-tracer')() //same thing 31 | ``` 32 | 33 | 34 | ## API 35 | 36 | ```js 37 | require('async-tracer') => (WritableStream, opts) => {enable, disable} 38 | ``` 39 | 40 | ```js 41 | require('async-tracer') => (String: path, ops) => {enable, disable} 42 | ``` 43 | 44 | ```js 45 | require('async-tracer') => (Number: handle, opts) => {enable, disable} 46 | ``` 47 | 48 | ### Interface 49 | 50 | #### `enable` 51 | 52 | Start tracing (will start automatically if `autostart` option is `true`) 53 | 54 | #### `disable` 55 | 56 | Stop all tracing 57 | 58 | ### Opts 59 | 60 | #### `autostart` [default: `true`] `Boolean` 61 | 62 | Begin tracing immediately 63 | 64 | #### `append` [default: `false`] `Boolean` 65 | 66 | Only applies to when a path is supplied, opens file with `a` flag 67 | instead of `w` flag. 68 | 69 | #### `prefix` [default: `undefined`] `Object` 70 | 71 | Additional data to attach to the beginning of each log message. 72 | 73 | #### `suffix` [default: `undefined`] `Object` 74 | 75 | Additional data to attach to the end of each log message. 76 | 77 | #### `stacks` [default: `false`] `Boolean` or `Number` 78 | 79 | If `true` then include an array of call sites in 80 | each `init` log, as the `stack` property. The stack array takes the following form: 81 | 82 | ``` 83 | ["functionName:fileName:lineNum:colNum"] 84 | ``` 85 | 86 | If set to a number, (from 1 to Infinity) `stacks` will also 87 | determine the maximum amount of frames to capture for the log 88 | (defaults to `Infinity` if `true`). 89 | 90 | #### `contexts` [default: `false`] `Boolean` 91 | 92 | Supply the operations context in the `pre` and `post` logs as the `ctx` property. 93 | The context is an exposed C object that holds state for the async op. 94 | 95 | 96 | ## Benchmarks 97 | 98 | Overhead of using `async-tracer` is about 25%. 99 | 100 | ```sh 101 | npm run benchmark 102 | ``` 103 | 104 | ### With tracing 105 | ``` 106 | Running 10s test @ http://localhost:3000 107 | 10 connections with 10 pipelining factor 108 | 109 | Stat Avg Stdev Max 110 | Latency (ms) 0.23 0.8 54 111 | Req/Sec 33283.64 2291.73 35135 112 | Bytes/Sec 3.7 MB 250.51 kB 3.93 MB 113 | ``` 114 | 115 | ### Without tracing 116 | ``` 117 | Running 10s test @ http://localhost:3000 118 | 10 connections with 10 pipelining factor 119 | 120 | Stat Avg Stdev Max 121 | Latency (ms) 0.13 0.44 33 122 | Req/Sec 45426.91 1279.75 46303 123 | Bytes/Sec 5.06 MB 150.72 kB 5.24 MB 124 | ``` 125 | 126 | Overhead of turning on tracing with `async_wrap` 127 | is around 8%, so the net overhead is 17%, mostly 128 | this is the cost of writing to a stream. 129 | 130 | ### Benchmarking Options 131 | 132 | The cost of turning `stacks` and `contexts` options on can also be determined with: 133 | 134 | ```sh 135 | npm run benchmark-options 136 | ``` 137 | 138 | Overhead of enabling context is surprisingly low, 4580k reqs without contexts 139 | 4370k reqs with context - about 5% overhead (profiled on Node 6.1.0, Mac OS X 2013, 2.6ghz i7, 16gb). 140 | 141 | However, YMMV based on real world usage. Another consideration of logging contexts is the log file 142 | size (although compression is likely to be quite effective). 143 | 144 | Overhead of enabling `stacks` is roughly the same as for enabling `contexts`. 145 | 146 | ## Example 147 | 148 | ```js 149 | var http = require('http') 150 | require('async-tracer')() 151 | 152 | http.createServer(function (req, res) { 153 | res.end('hello world') 154 | }).listen(3000) 155 | ``` 156 | 157 | ```sh 158 | curl http://localhost:3000 159 | ``` 160 | 161 | ## Test 162 | 163 | ```sh 164 | npm test 165 | ``` 166 | 167 | ## License 168 | 169 | MIT 170 | 171 | ## Acknowledgements 172 | 173 | Sponsored by [nearForm](http://nearform.com) -------------------------------------------------------------------------------- /smoke.js: -------------------------------------------------------------------------------- 1 | var dns = require('dns') 2 | var trace = require('./')(process.stdout) 3 | 4 | 5 | process.stdin.resume() 6 | dns.lookup('example.com', () => { 7 | setTimeout(() => { 8 | process.stdin.destroy() 9 | }, 100) 10 | }) 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var setFlagsFromString = require('v8').setFlagsFromString 2 | 3 | if (process.versions.node[0] !== '6') { 4 | setFlagsFromString('--harmony-destructuring') 5 | } 6 | 7 | require('./test') 8 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const PORT = 12346 3 | const domain = require('domain') 4 | const {test} = require('tap') 5 | const net = require('net') 6 | const dgram = require('dgram') 7 | const fs = require('fs') 8 | const {parse} = JSON 9 | const fivePlus = process.versions.node[0] === '6' || process.versions.node[0] === '5' 10 | const fiveTenPlus = fivePlus && +process.versions.node[2] >= 10 11 | 12 | const tracer = require('../') 13 | 14 | tracer('/dev/null', { 15 | suffix: {some: 'data'}, 16 | prefix: {other: 'data'}, 17 | stacks: true, 18 | contexts: true 19 | }) 20 | 21 | fs.write = (f, s, cb) => { 22 | checker(f, s) 23 | cb() 24 | } 25 | 26 | const checker = (f, s) => { 27 | let fn = checker.q.shift() 28 | if (fn) { fn(s) } 29 | } 30 | checker.q = [] 31 | 32 | const check = (fn) => { 33 | if (Array.isArray(fn)) { 34 | checker.q.push.apply(checker.q, fn) 35 | return 36 | } 37 | checker.q.push(fn) 38 | } 39 | 40 | test('net', ({is, end}) => { 41 | process.nextTick(() => { 42 | const server = net.createServer((s) => { 43 | s.end() 44 | server.close() 45 | }) 46 | 47 | server.listen(PORT) 48 | 49 | process.nextTick(() => { 50 | const client = net.connect(PORT) 51 | 52 | client.end('meow') 53 | }) 54 | }) 55 | 56 | check([ 57 | (s) => { 58 | const {op, phase} = parse(s) 59 | is(op, 'TCP') 60 | is(phase, 'init') 61 | }, 62 | (s) => { 63 | const {op, phase} = parse(s) 64 | is(op, 'TCP') 65 | is(phase, 'init') 66 | }, 67 | (s) => { 68 | const {op, phase} = parse(s) 69 | is(op, 'GETADDRINFOREQ') 70 | is(phase, 'init') 71 | }, 72 | (s) => { 73 | const {op, phase} = parse(s) 74 | is(op, 'GETADDRINFOREQ') 75 | is(phase, 'pre') 76 | }, 77 | (s) => { 78 | const {op, phase} = parse(s) 79 | is(op, 'TCPCONNECT') 80 | is(phase, 'init') 81 | }, 82 | (s) => { 83 | const {op, phase} = parse(s) 84 | is(op, 'GETADDRINFOREQ') 85 | is(phase, 'post') 86 | }, 87 | (s) => { 88 | const {op, phase} = parse(s) 89 | is(op, 'GETADDRINFOREQ') 90 | is(phase, 'destroy') 91 | }, 92 | (s) => { 93 | const {op, phase} = parse(s) 94 | is(op, 'TCP') 95 | is(phase, 'init') 96 | }, 97 | (s) => { 98 | const {op, phase} = parse(s) 99 | is(op, 'TCP') 100 | is(phase, 'pre') 101 | }, 102 | (s) => { 103 | const {op, phase} = parse(s) 104 | is(op, 'SHUTDOWN') 105 | is(phase, 'init') 106 | }, 107 | (s) => { 108 | const {op, phase} = parse(s) 109 | is(op, 'TCP') 110 | is(phase, 'post') 111 | }, 112 | (s) => { 113 | const {op, phase} = parse(s) 114 | is(op, 'TCPCONNECT') 115 | is(phase, 'pre') 116 | }, 117 | (s) => { 118 | const {op, phase} = parse(s) 119 | is(op, 'SHUTDOWN') 120 | is(phase, 'init') 121 | }, 122 | (s) => { 123 | const {op, phase} = parse(s) 124 | is(op, 'TCPCONNECT') 125 | is(phase, 'post') 126 | }, 127 | (s) => { 128 | const {op, phase} = parse(s) 129 | is(op, 'TCPCONNECT') 130 | is(phase, 'destroy') 131 | }, 132 | (s) => { 133 | const {op, phase} = parse(s) 134 | is(op, 'TCP') 135 | is(phase, 'destroy') 136 | }, 137 | (s) => { 138 | const {op, phase} = parse(s) 139 | is(op, 'SHUTDOWN') 140 | is(phase, 'pre') 141 | }, 142 | (s) => { 143 | const {op, phase} = parse(s) 144 | is(op, 'SHUTDOWN') 145 | is(phase, 'post') 146 | }, 147 | (s) => { 148 | const {op, phase} = parse(s) 149 | is(op, 'SHUTDOWN') 150 | is(phase, 'destroy') 151 | }, 152 | (s) => { 153 | const {op, phase} = parse(s) 154 | is(op, 'TCP') 155 | is(phase, 'pre') 156 | }, 157 | (s) => { 158 | const {op, phase} = parse(s) 159 | is(op, 'TCP') 160 | is(phase, 'post') 161 | }, 162 | (s) => { 163 | const {op, phase} = parse(s) 164 | is(op, 'SHUTDOWN') 165 | is(phase, 'pre') 166 | }, 167 | (s) => { 168 | const {op, phase} = parse(s) 169 | is(op, 'SHUTDOWN') 170 | is(phase, 'post') 171 | }, 172 | (s) => { 173 | const {op, phase} = parse(s) 174 | is(op, 'SHUTDOWN') 175 | is(phase, 'destroy') 176 | }, 177 | (s) => { 178 | const {op, phase} = parse(s) 179 | is(op, 'TCP') 180 | is(phase, 'pre') 181 | }, 182 | (s) => { 183 | const {op, phase} = parse(s) 184 | is(op, 'TCP') 185 | is(phase, 'post') 186 | }, 187 | (s) => { 188 | const {op, phase} = parse(s) 189 | is(op, 'TCP') 190 | is(phase, 'pre') 191 | }, 192 | (s) => { 193 | const {op, phase} = parse(s) 194 | is(op, 'TCP') 195 | is(phase, 'post') 196 | }, 197 | (s) => { 198 | const {op, phase} = parse(s) 199 | is(op, 'TCP') 200 | is(phase, 'pre') 201 | }, 202 | (s) => { 203 | const {op, phase} = parse(s) 204 | is(op, 'TCP') 205 | is(phase, 'post') 206 | }, 207 | (s) => { 208 | const {op, phase} = parse(s) 209 | is(op, 'TCP') 210 | is(phase, 'destroy') 211 | }, 212 | (s) => { 213 | const {op, phase} = parse(s) 214 | is(op, 'TCP') 215 | is(phase, 'pre') 216 | }, 217 | (s) => { 218 | const {op, phase} = parse(s) 219 | is(op, 'TCP') 220 | is(phase, 'post') 221 | }, 222 | (s) => { 223 | const {op, phase} = parse(s) 224 | is(op, 'TCP') 225 | is(phase, 'destroy') 226 | end() 227 | } 228 | ]) 229 | }) 230 | 231 | test('setTimeout', ({is, end}) => { 232 | check([ 233 | (s) => { 234 | const {op, phase} = parse(s) 235 | is(op, 'TIMER') 236 | is(phase, 'init') 237 | }, 238 | (s) => { 239 | const {op, phase} = parse(s) 240 | is(op, 'TIMER') 241 | is(phase, 'pre') 242 | }, 243 | (s) => { 244 | const {op, phase} = parse(s) 245 | is(op, 'TIMER') 246 | is(phase, 'post') 247 | }, 248 | (s) => { 249 | const {op, phase} = parse(s) 250 | is(op, 'TIMER') 251 | is(phase, 'destroy') 252 | end() 253 | } 254 | ]) 255 | process.nextTick(() => setTimeout(() => {})) 256 | }) 257 | 258 | if (fiveTenPlus) { 259 | test('threw', ({is, teardown, end}) => { 260 | check([ 261 | null, 262 | null, 263 | (s) => { 264 | const {op, phase, threw} = parse(s) 265 | is(op, 'TIMER') 266 | is(phase, 'post') 267 | is(threw, true) 268 | end() 269 | } 270 | ]) 271 | 272 | process.nextTick(() => 273 | domain.create() 274 | .on('error', () => {}) 275 | .run(() => { 276 | setTimeout(() => { 277 | throw Error() 278 | }) 279 | }) 280 | ) 281 | }) 282 | } 283 | 284 | test('dgram', ({is, end}) => { 285 | process.nextTick(() => { 286 | const server = dgram.createSocket('udp4') 287 | server.bind(PORT) 288 | server.close() 289 | }) 290 | 291 | check([ 292 | (s) => { 293 | const {op, phase} = parse(s) 294 | is(op, 'UDP') 295 | is(phase, 'init') 296 | }, 297 | (s) => { 298 | const {op, phase} = parse(s) 299 | is(op, 'UDP') 300 | is(phase, 'destroy') 301 | end() 302 | } 303 | ]) 304 | }) 305 | 306 | test('fs', ({is, end}) => { 307 | process.nextTick(() => { 308 | fs.stat('/dev/random', () => { }) 309 | }) 310 | 311 | check([ 312 | null, // ignore stray timer op in 5+ 313 | (s) => { 314 | const {op, phase} = parse(s) 315 | is(op, 'FSREQ') 316 | is(phase, 'init') 317 | }, 318 | (s) => { 319 | const {op, phase} = parse(s) 320 | is(op, 'FSREQ') 321 | is(phase, 'pre') 322 | }, 323 | (s) => { 324 | const {op, phase} = parse(s) 325 | is(op, 'FSREQ') 326 | is(phase, 'post') 327 | }, 328 | (s) => { 329 | const {op, phase} = parse(s) 330 | is(op, 'FSREQ') 331 | is(phase, 'destroy') 332 | end() 333 | } 334 | ].slice(fiveTenPlus ? 0 : 1)) 335 | }) 336 | 337 | test('prefix & suffix', ({is, end}) => { 338 | check([ 339 | (s) => { 340 | const {some, other} = parse(s) 341 | is(some, 'data') 342 | is(other, 'data') 343 | end() 344 | } 345 | ]) 346 | 347 | process.nextTick(() => { 348 | setTimeout(() => {}) 349 | }) 350 | }) 351 | 352 | test('prefix & suffix', ({is, end}) => { 353 | check([ 354 | (s) => { 355 | const {some, other} = parse(s) 356 | is(some, 'data') 357 | is(other, 'data') 358 | end() 359 | } 360 | ]) 361 | 362 | process.nextTick(() => { 363 | setTimeout(() => {}) 364 | }) 365 | }) 366 | 367 | // test('opts.stacks', ({is, ok, end}) => { 368 | // check([ 369 | // (s) => { 370 | // const {phase, stack} = parse(s) 371 | // is(phase, 'init') 372 | // ok(stack) 373 | // end() 374 | // } 375 | // ]) 376 | // process.nextTick(() => { 377 | // setTimeout(() => {}) 378 | // }) 379 | // }) 380 | 381 | // test('opts.context', ({is, ok, end}) => { 382 | // check([ 383 | // (s) => { 384 | // const {phase, ctx} = parse(s) 385 | // is(phase, 'pre') 386 | // ok(ctx) 387 | // }, 388 | // (s) => { 389 | // const {phase, ctx} = parse(s) 390 | // is(phase, 'post') 391 | // ok(ctx) 392 | // end() 393 | // } 394 | // ]) 395 | // process.nextTick(() => { 396 | // setTimeout(() => {}) 397 | // }) 398 | // }) 399 | 400 | -------------------------------------------------------------------------------- /wrappers.md: -------------------------------------------------------------------------------- 1 | 2 | ## Provider -> Context mappings 3 | 4 | ``` 5 | NONE: 0, 6 | CRYPTO: 1, -> process.binding('crypto').randomBytes(0, function(){}).constructor 7 | FSEVENTWRAP: 2, -> process.binding('fs_event_wrap').FSEvent 8 | FSREQWRAP: 3, -> process.binding('fs').FSReqWrap 9 | GETADDRINFOREQWRAP: 4, -> process.binding('cares_wrap').GetAddrInfoReqWrap 10 | GETNAMEINFOREQWRAP: 5, -> process.binding('cares_wrap').GetNameInfoReqWrap 11 | HTTPPARSER: 6, -> process.binding('http_parser').HTTPParser 12 | JSSTREAM: 7, -> process.binding('js_stream').JSStream 13 | PIPEWRAP: 8, -> process.binding('pipe_wrap').Pipe 14 | PIPECONNECTWRAP: 9, -> process.binding('pipe_wrap').PipeConnectWrap 15 | PROCESSWRAP: 10, -> process.binding('process_wrap').Process 16 | QUERYWRAP: 11, -> process.binding('cares_wrap').QueryReqWrap 17 | SHUTDOWNWRAP: 12, -> process.binding('stream_wrap').ShutdownWrap 18 | SIGNALWRAP: 13, -> process.binding('signal_wrap').Signal 19 | STATWATCHER: 14, -> process.binding('fs').StatWatcher 20 | TCPWRAP: 15, -> process.binding('tcp_wrap').TCP 21 | TCPCONNECTWRAP: 16, -> process.binding('tcp_wrap').TCPConnectWrap 22 | TIMERWRAP: 17, -> process.binding('timer_wrap').Timer 23 | TLSWRAP: 18, -> process.binding('tls_wrap').TLSWrap (process.binding('tls_wrap').wrap ?) 24 | TTYWRAP: 19, -> process.binding('tty_wrap').TTY 25 | UDPWRAP: 20, -> process.binding('udp_wrap').UDP 26 | UDPSENDWRAP: 21, -> process.binding('udp_wrap').SendWrap 27 | WRITEWRAP: 22, -> process.binding('stream_wrap').WriteWrap 28 | ZLIB: 23 -> process.binding('zlib').Zlib 29 | ``` 30 | 31 | ## Provider -> Area mappings 32 | 33 | ``` 34 | NONE: 0, -> misc 35 | CRYPTO: 1, -> crypto 36 | FSEVENTWRAP: 2, -> fs 37 | FSREQWRAP: 3, -> fs 38 | GETADDRINFOREQWRAP: 4, -> dns 39 | GETNAMEINFOREQWRAP: 5, -> dns 40 | HTTPPARSER: 6, -> http 41 | JSSTREAM: 7, -> stream 42 | PIPEWRAP: 8, -> stream 43 | PIPECONNECTWRAP: 9, -> stream 44 | PROCESSWRAP: 10, -> process 45 | QUERYWRAP: 11, -> dns 46 | SHUTDOWNWRAP: 12, -> stream 47 | SIGNALWRAP: 13, -> process 48 | STATWATCHER: 14, -> fs 49 | TCPWRAP: 15, -> net 50 | TCPCONNECTWRAP: 16, -> net 51 | TIMERWRAP: 17, -> timers 52 | TLSWRAP: 18, -> tls 53 | TTYWRAP: 19, -> tty 54 | UDPWRAP: 20, -> dgram 55 | UDPSENDWRAP: 21, -> dgram 56 | WRITEWRAP: 22, -> stream 57 | ZLIB: 23 -> zlib 58 | ``` 59 | 60 | ## Area -> Provider Number mappings 61 | 62 | ``` 63 | 0: misc 64 | 1: crypto 65 | 2: fs 66 | 3: fs 67 | 4: dns 68 | 5: dns 69 | 6: http 70 | 7: stream 71 | 8: stream 72 | 9: stream 73 | 10: process 74 | 11: dns 75 | 12: stream 76 | 13: process 77 | 14: fs 78 | 15: net 79 | 16: net 80 | 17: timers 81 | 18: tls 82 | 19: tty 83 | 20: dgram 84 | 21: dgram 85 | 22: stream 86 | 23: zlib 87 | ``` 88 | --------------------------------------------------------------------------------