From 0ca876ac964c5948ece56c9c417b2e171769aa71 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 8 Apr 2021 03:49:53 -0700 Subject: [PATCH 01/12] debugger: move node-inspect to internal library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node-inspect developers have agreed to move node-inspect into core rather than vendor it as a dependency. Refs: https://github.com/nodejs/node/discussions/36481 PR-URL: https://github.com/nodejs/node/pull/38161 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- lib/internal/inspector/_inspect.js | 369 +++++++ lib/internal/inspector/inspect_client.js | 355 +++++++ lib/internal/inspector/inspect_repl.js | 1112 ++++++++++++++++++++++ lib/internal/main/inspect.js | 2 +- node.gyp | 6 +- 5 files changed, 1840 insertions(+), 4 deletions(-) create mode 100644 lib/internal/inspector/_inspect.js create mode 100644 lib/internal/inspector/inspect_client.js create mode 100644 lib/internal/inspector/inspect_repl.js diff --git a/lib/internal/inspector/_inspect.js b/lib/internal/inspector/_inspect.js new file mode 100644 index 00000000000000..df4e53979db8fd --- /dev/null +++ b/lib/internal/inspector/_inspect.js @@ -0,0 +1,369 @@ +/* + * Copyright Node.js contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +// TODO(trott): enable ESLint +/* eslint-disable */ + +'use strict'; +const { spawn } = require('child_process'); +const { EventEmitter } = require('events'); +const net = require('net'); +const util = require('util'); + +const runAsStandalone = typeof __dirname !== 'undefined'; + +const { 0: InspectClient, 1: createRepl } = + runAsStandalone ? + // This copy of node-inspect is on-disk, relative paths make sense. + [ + require('./inspect_client'), + require('./inspect_repl'), + ] : + // This copy of node-inspect is built into the node executable. + [ + require('internal/inspector/inspect_client'), + require('internal/inspector/inspect_repl'), + ]; + +const debuglog = util.debuglog('inspect'); + +class StartupError extends Error { + constructor(message) { + super(message); + this.name = 'StartupError'; + } +} + +function portIsFree(host, port, timeout = 9999) { + if (port === 0) return Promise.resolve(); // Binding to a random port. + + const retryDelay = 150; + let didTimeOut = false; + + return new Promise((resolve, reject) => { + setTimeout(() => { + didTimeOut = true; + reject(new StartupError( + `Timeout (${timeout}) waiting for ${host}:${port} to be free`)); + }, timeout); + + function pingPort() { + if (didTimeOut) return; + + const socket = net.connect(port, host); + let didRetry = false; + function retry() { + if (!didRetry && !didTimeOut) { + didRetry = true; + setTimeout(pingPort, retryDelay); + } + } + + socket.on('error', (error) => { + if (error.code === 'ECONNREFUSED') { + resolve(); + } else { + retry(); + } + }); + socket.on('connect', () => { + socket.destroy(); + retry(); + }); + } + pingPort(); + }); +} + +function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) { + return portIsFree(inspectHost, inspectPort) + .then(() => { + return new Promise((resolve) => { + const needDebugBrk = process.version.match(/^v(6|7)\./); + const args = (needDebugBrk ? + ['--inspect', `--debug-brk=${inspectPort}`] : + [`--inspect-brk=${inspectPort}`]) + .concat([script], scriptArgs); + const child = spawn(process.execPath, args); + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + child.stdout.on('data', childPrint); + child.stderr.on('data', childPrint); + + let output = ''; + function waitForListenHint(text) { + output += text; + if (/Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//.test(output)) { + const host = RegExp.$1; + const port = Number.parseInt(RegExp.$2); + child.stderr.removeListener('data', waitForListenHint); + resolve([child, port, host]); + } + } + + child.stderr.on('data', waitForListenHint); + }); + }); +} + +function createAgentProxy(domain, client) { + const agent = new EventEmitter(); + agent.then = (...args) => { + // TODO: potentially fetch the protocol and pretty-print it here. + const descriptor = { + [util.inspect.custom](depth, { stylize }) { + return stylize(`[Agent ${domain}]`, 'special'); + }, + }; + return Promise.resolve(descriptor).then(...args); + }; + + return new Proxy(agent, { + get(target, name) { + if (name in target) return target[name]; + return function callVirtualMethod(params) { + return client.callMethod(`${domain}.${name}`, params); + }; + }, + }); +} + +class NodeInspector { + constructor(options, stdin, stdout) { + this.options = options; + this.stdin = stdin; + this.stdout = stdout; + + this.paused = true; + this.child = null; + + if (options.script) { + this._runScript = runScript.bind(null, + options.script, + options.scriptArgs, + options.host, + options.port, + this.childPrint.bind(this)); + } else { + this._runScript = + () => Promise.resolve([null, options.port, options.host]); + } + + this.client = new InspectClient(); + + this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime']; + this.domainNames.forEach((domain) => { + this[domain] = createAgentProxy(domain, this.client); + }); + this.handleDebugEvent = (fullName, params) => { + const { 0: domain, 1: name } = fullName.split('.'); + if (domain in this) { + this[domain].emit(name, params); + } + }; + this.client.on('debugEvent', this.handleDebugEvent); + const startRepl = createRepl(this); + + // Handle all possible exits + process.on('exit', () => this.killChild()); + process.once('SIGTERM', process.exit.bind(process, 0)); + process.once('SIGHUP', process.exit.bind(process, 0)); + + this.run() + .then(() => startRepl()) + .then((repl) => { + this.repl = repl; + this.repl.on('exit', () => { + process.exit(0); + }); + this.paused = false; + }) + .then(null, (error) => process.nextTick(() => { throw error; })); + } + + suspendReplWhile(fn) { + if (this.repl) { + this.repl.pause(); + } + this.stdin.pause(); + this.paused = true; + return new Promise((resolve) => { + resolve(fn()); + }).then(() => { + this.paused = false; + if (this.repl) { + this.repl.resume(); + this.repl.displayPrompt(); + } + this.stdin.resume(); + }).then(null, (error) => process.nextTick(() => { throw error; })); + } + + killChild() { + this.client.reset(); + if (this.child) { + this.child.kill(); + this.child = null; + } + } + + run() { + this.killChild(); + + return this._runScript().then(({ 0: child, 1: port, 2: host }) => { + this.child = child; + + let connectionAttempts = 0; + const attemptConnect = () => { + ++connectionAttempts; + debuglog('connection attempt #%d', connectionAttempts); + this.stdout.write('.'); + return this.client.connect(port, host) + .then(() => { + debuglog('connection established'); + this.stdout.write(' ok'); + }, (error) => { + debuglog('connect failed', error); + // If it's failed to connect 10 times then print failed message + if (connectionAttempts >= 10) { + this.stdout.write(' failed to connect, please retry\n'); + process.exit(1); + } + + return new Promise((resolve) => setTimeout(resolve, 500)) + .then(attemptConnect); + }); + }; + + this.print(`connecting to ${host}:${port} ..`, true); + return attemptConnect(); + }); + } + + clearLine() { + if (this.stdout.isTTY) { + this.stdout.cursorTo(0); + this.stdout.clearLine(1); + } else { + this.stdout.write('\b'); + } + } + + print(text, oneline = false) { + this.clearLine(); + this.stdout.write(oneline ? text : `${text}\n`); + } + + childPrint(text) { + this.print( + text.toString() + .split(/\r\n|\r|\n/g) + .filter((chunk) => !!chunk) + .map((chunk) => `< ${chunk}`) + .join('\n') + ); + if (!this.paused) { + this.repl.displayPrompt(true); + } + if (/Waiting for the debugger to disconnect\.\.\.\n$/.test(text)) { + this.killChild(); + } + } +} + +function parseArgv([target, ...args]) { + let host = '127.0.0.1'; + let port = 9229; + let isRemote = false; + let script = target; + let scriptArgs = args; + + const hostMatch = target.match(/^([^:]+):(\d+)$/); + const portMatch = target.match(/^--port=(\d+)$/); + + if (hostMatch) { + // Connecting to remote debugger + host = hostMatch[1]; + port = parseInt(hostMatch[2], 10); + isRemote = true; + script = null; + } else if (portMatch) { + // Start on custom port + port = parseInt(portMatch[1], 10); + script = args[0]; + scriptArgs = args.slice(1); + } else if (args.length === 1 && /^\d+$/.test(args[0]) && target === '-p') { + // Start debugger against a given pid + const pid = parseInt(args[0], 10); + try { + process._debugProcess(pid); + } catch (e) { + if (e.code === 'ESRCH') { + console.error(`Target process: ${pid} doesn't exist.`); + process.exit(1); + } + throw e; + } + script = null; + isRemote = true; + } + + return { + host, port, isRemote, script, scriptArgs, + }; +} + +function startInspect(argv = process.argv.slice(2), + stdin = process.stdin, + stdout = process.stdout) { + if (argv.length < 1) { + const invokedAs = runAsStandalone ? + 'node-inspect' : + `${process.argv0} ${process.argv[1]}`; + + console.error(`Usage: ${invokedAs} script.js`); + console.error(` ${invokedAs} :`); + console.error(` ${invokedAs} -p `); + process.exit(1); + } + + const options = parseArgv(argv); + const inspector = new NodeInspector(options, stdin, stdout); + + stdin.resume(); + + function handleUnexpectedError(e) { + if (!(e instanceof StartupError)) { + console.error('There was an internal error in Node.js. ' + + 'Please report this bug.'); + console.error(e.message); + console.error(e.stack); + } else { + console.error(e.message); + } + if (inspector.child) inspector.child.kill(); + process.exit(1); + } + + process.on('uncaughtException', handleUnexpectedError); +} +exports.start = startInspect; diff --git a/lib/internal/inspector/inspect_client.js b/lib/internal/inspector/inspect_client.js new file mode 100644 index 00000000000000..6010f4ac09389d --- /dev/null +++ b/lib/internal/inspector/inspect_client.js @@ -0,0 +1,355 @@ +/* + * Copyright Node.js contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +// TODO(trott): enable ESLint +/* eslint-disable */ + +'use strict'; +const Buffer = require('buffer').Buffer; +const { EventEmitter } = require('events'); +const http = require('http'); +const URL = require('url'); +const util = require('util'); + +const debuglog = util.debuglog('inspect'); + +const kOpCodeText = 0x1; +const kOpCodeClose = 0x8; + +const kFinalBit = 0x80; +const kReserved1Bit = 0x40; +const kReserved2Bit = 0x20; +const kReserved3Bit = 0x10; +const kOpCodeMask = 0xF; +const kMaskBit = 0x80; +const kPayloadLengthMask = 0x7F; + +const kMaxSingleBytePayloadLength = 125; +const kMaxTwoBytePayloadLength = 0xFFFF; +const kTwoBytePayloadLengthField = 126; +const kEightBytePayloadLengthField = 127; +const kMaskingKeyWidthInBytes = 4; + +function isEmpty(obj) { + return Object.keys(obj).length === 0; +} + +function unpackError({ code, message, data }) { + const err = new Error(`${message} - ${data}`); + err.code = code; + Error.captureStackTrace(err, unpackError); + return err; +} + +function encodeFrameHybi17(payload) { + var i; + + const dataLength = payload.length; + + let singleByteLength; + let additionalLength; + if (dataLength > kMaxTwoBytePayloadLength) { + singleByteLength = kEightBytePayloadLengthField; + additionalLength = Buffer.alloc(8); + let remaining = dataLength; + for (i = 0; i < 8; ++i) { + additionalLength[7 - i] = remaining & 0xFF; + remaining >>= 8; + } + } else if (dataLength > kMaxSingleBytePayloadLength) { + singleByteLength = kTwoBytePayloadLengthField; + additionalLength = Buffer.alloc(2); + additionalLength[0] = (dataLength & 0xFF00) >> 8; + additionalLength[1] = dataLength & 0xFF; + } else { + additionalLength = Buffer.alloc(0); + singleByteLength = dataLength; + } + + const header = Buffer.from([ + kFinalBit | kOpCodeText, + kMaskBit | singleByteLength, + ]); + + const mask = Buffer.alloc(4); + const masked = Buffer.alloc(dataLength); + for (i = 0; i < dataLength; ++i) { + masked[i] = payload[i] ^ mask[i % kMaskingKeyWidthInBytes]; + } + + return Buffer.concat([header, additionalLength, mask, masked]); +} + +function decodeFrameHybi17(data) { + const dataAvailable = data.length; + const notComplete = { closed: false, payload: null, rest: data }; + let payloadOffset = 2; + if ((dataAvailable - payloadOffset) < 0) return notComplete; + + const firstByte = data[0]; + const secondByte = data[1]; + + const final = (firstByte & kFinalBit) !== 0; + const reserved1 = (firstByte & kReserved1Bit) !== 0; + const reserved2 = (firstByte & kReserved2Bit) !== 0; + const reserved3 = (firstByte & kReserved3Bit) !== 0; + const opCode = firstByte & kOpCodeMask; + const masked = (secondByte & kMaskBit) !== 0; + const compressed = reserved1; + if (compressed) { + throw new Error('Compressed frames not supported'); + } + if (!final || reserved2 || reserved3) { + throw new Error('Only compression extension is supported'); + } + + if (masked) { + throw new Error('Masked server frame - not supported'); + } + + let closed = false; + switch (opCode) { + case kOpCodeClose: + closed = true; + break; + case kOpCodeText: + break; + default: + throw new Error(`Unsupported op code ${opCode}`); + } + + let payloadLength = secondByte & kPayloadLengthMask; + switch (payloadLength) { + case kTwoBytePayloadLengthField: + payloadOffset += 2; + payloadLength = (data[2] << 8) + data[3]; + break; + + case kEightBytePayloadLengthField: + payloadOffset += 8; + payloadLength = 0; + for (var i = 0; i < 8; ++i) { + payloadLength <<= 8; + payloadLength |= data[2 + i]; + } + break; + + default: + // Nothing. We already have the right size. + } + if ((dataAvailable - payloadOffset - payloadLength) < 0) return notComplete; + + const payloadEnd = payloadOffset + payloadLength; + return { + payload: data.slice(payloadOffset, payloadEnd), + rest: data.slice(payloadEnd), + closed, + }; +} + +class Client extends EventEmitter { + constructor() { + super(); + this.handleChunk = this._handleChunk.bind(this); + + this._port = undefined; + this._host = undefined; + + this.reset(); + } + + _handleChunk(chunk) { + this._unprocessed = Buffer.concat([this._unprocessed, chunk]); + + while (this._unprocessed.length > 2) { + const { + closed, + payload: payloadBuffer, + rest + } = decodeFrameHybi17(this._unprocessed); + this._unprocessed = rest; + + if (closed) { + this.reset(); + return; + } + if (payloadBuffer === null || payloadBuffer.length === 0) break; + + const payloadStr = payloadBuffer.toString(); + debuglog('< %s', payloadStr); + const lastChar = payloadStr[payloadStr.length - 1]; + if (payloadStr[0] !== '{' || lastChar !== '}') { + throw new Error(`Payload does not look like JSON: ${payloadStr}`); + } + let payload; + try { + payload = JSON.parse(payloadStr); + } catch (parseError) { + parseError.string = payloadStr; + throw parseError; + } + + const { id, method, params, result, error } = payload; + if (id) { + const handler = this._pending[id]; + if (handler) { + delete this._pending[id]; + handler(error, result); + } + } else if (method) { + this.emit('debugEvent', method, params); + this.emit(method, params); + } else { + throw new Error(`Unsupported response: ${payloadStr}`); + } + } + } + + reset() { + if (this._http) { + this._http.destroy(); + } + this._http = null; + this._lastId = 0; + this._socket = null; + this._pending = {}; + this._unprocessed = Buffer.alloc(0); + } + + callMethod(method, params) { + return new Promise((resolve, reject) => { + if (!this._socket) { + reject(new Error('Use `run` to start the app again.')); + return; + } + const data = { id: ++this._lastId, method, params }; + this._pending[data.id] = (error, result) => { + if (error) reject(unpackError(error)); + else resolve(isEmpty(result) ? undefined : result); + }; + const json = JSON.stringify(data); + debuglog('> %s', json); + this._socket.write(encodeFrameHybi17(Buffer.from(json))); + }); + } + + _fetchJSON(urlPath) { + return new Promise((resolve, reject) => { + const httpReq = http.get({ + host: this._host, + port: this._port, + path: urlPath, + }); + + const chunks = []; + + function onResponse(httpRes) { + function parseChunks() { + const resBody = Buffer.concat(chunks).toString(); + if (httpRes.statusCode !== 200) { + reject(new Error(`Unexpected ${httpRes.statusCode}: ${resBody}`)); + return; + } + try { + resolve(JSON.parse(resBody)); + } catch (parseError) { + reject(new Error(`Response didn't contain JSON: ${resBody}`)); + + } + } + + httpRes.on('error', reject); + httpRes.on('data', (chunk) => chunks.push(chunk)); + httpRes.on('end', parseChunks); + } + + httpReq.on('error', reject); + httpReq.on('response', onResponse); + }); + } + + connect(port, host) { + this._port = port; + this._host = host; + return this._discoverWebsocketPath() + .then((urlPath) => this._connectWebsocket(urlPath)); + } + + _discoverWebsocketPath() { + return this._fetchJSON('/json') + .then(({ 0: { webSocketDebuggerUrl } }) => + URL.parse(webSocketDebuggerUrl).path); + } + + _connectWebsocket(urlPath) { + this.reset(); + + const key1 = require('crypto').randomBytes(16).toString('base64'); + debuglog('request websocket', key1); + + const httpReq = this._http = http.request({ + host: this._host, + port: this._port, + path: urlPath, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': key1, + 'Sec-WebSocket-Version': '13', + }, + }); + httpReq.on('error', (e) => { + this.emit('error', e); + }); + httpReq.on('response', (httpRes) => { + if (httpRes.statusCode >= 400) { + process.stderr.write(`Unexpected HTTP code: ${httpRes.statusCode}\n`); + httpRes.pipe(process.stderr); + } else { + httpRes.pipe(process.stderr); + } + }); + + const handshakeListener = (res, socket) => { + // TODO: we *could* validate res.headers[sec-websocket-accept] + debuglog('websocket upgrade'); + + this._socket = socket; + socket.on('data', this.handleChunk); + socket.on('close', () => { + this.emit('close'); + }); + + this.emit('ready'); + }; + + return new Promise((resolve, reject) => { + this.once('error', reject); + this.once('ready', resolve); + + httpReq.on('upgrade', handshakeListener); + httpReq.end(); + }); + } +} + +module.exports = Client; diff --git a/lib/internal/inspector/inspect_repl.js b/lib/internal/inspector/inspect_repl.js new file mode 100644 index 00000000000000..7d5b019e28506d --- /dev/null +++ b/lib/internal/inspector/inspect_repl.js @@ -0,0 +1,1112 @@ +/* + * Copyright Node.js contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +// TODO(trott): enable ESLint +/* eslint-disable */ + +'use strict'; +const FS = require('fs'); +const Path = require('path'); +const Repl = require('repl'); +const util = require('util'); +const vm = require('vm'); +const fileURLToPath = require('url').fileURLToPath; + +const debuglog = util.debuglog('inspect'); + +const SHORTCUTS = { + cont: 'c', + next: 'n', + step: 's', + out: 'o', + backtrace: 'bt', + setBreakpoint: 'sb', + clearBreakpoint: 'cb', + run: 'r', +}; + +const HELP = ` +run, restart, r Run the application or reconnect +kill Kill a running application or disconnect + +cont, c Resume execution +next, n Continue to next line in current file +step, s Step into, potentially entering a function +out, o Step out, leaving the current function +backtrace, bt Print the current backtrace +list Print the source around the current line where execution + is currently paused + +setBreakpoint, sb Set a breakpoint +clearBreakpoint, cb Clear a breakpoint +breakpoints List all known breakpoints +breakOnException Pause execution whenever an exception is thrown +breakOnUncaught Pause execution whenever an exception isn't caught +breakOnNone Don't pause on exceptions (this is the default) + +watch(expr) Start watching the given expression +unwatch(expr) Stop watching an expression +watchers Print all watched expressions and their current values + +exec(expr) Evaluate the expression and print the value +repl Enter a debug repl that works like exec + +scripts List application scripts that are currently loaded +scripts(true) List all scripts (including node-internals) + +profile Start CPU profiling session. +profileEnd Stop current CPU profiling session. +profiles Array of completed CPU profiling sessions. +profiles[n].save(filepath = 'node.cpuprofile') + Save CPU profiling session to disk as JSON. + +takeHeapSnapshot(filepath = 'node.heapsnapshot') + Take a heap snapshot and save to disk as JSON. +`.trim(); + +const FUNCTION_NAME_PATTERN = /^(?:function\*? )?([^(\s]+)\(/; +function extractFunctionName(description) { + const fnNameMatch = description.match(FUNCTION_NAME_PATTERN); + return fnNameMatch ? `: ${fnNameMatch[1]}` : ''; +} + +const PUBLIC_BUILTINS = require('module').builtinModules; +const NATIVES = PUBLIC_BUILTINS ? process.binding('natives') : {}; +function isNativeUrl(url) { + url = url.replace(/\.js$/, ''); + if (PUBLIC_BUILTINS) { + if (url.startsWith('internal/') || PUBLIC_BUILTINS.includes(url)) + return true; + } + + return url in NATIVES || url === 'bootstrap_node'; +} + +function getRelativePath(filenameOrURL) { + const dir = Path.join(Path.resolve(), 'x').slice(0, -1); + + const filename = filenameOrURL.startsWith('file://') ? + fileURLToPath(filenameOrURL) : filenameOrURL; + + // Change path to relative, if possible + if (filename.indexOf(dir) === 0) { + return filename.slice(dir.length); + } + return filename; +} + +function toCallback(promise, callback) { + function forward(...args) { + process.nextTick(() => callback(...args)); + } + promise.then(forward.bind(null, null), forward); +} + +// Adds spaces and prefix to number +// maxN is a maximum number we should have space for +function leftPad(n, prefix, maxN) { + const s = n.toString(); + const nchars = Math.max(2, String(maxN).length) + 1; + const nspaces = nchars - s.length - 1; + + return prefix + ' '.repeat(nspaces) + s; +} + +function markSourceColumn(sourceText, position, useColors) { + if (!sourceText) return ''; + + const head = sourceText.slice(0, position); + let tail = sourceText.slice(position); + + // Colourize char if stdout supports colours + if (useColors) { + tail = tail.replace(/(.+?)([^\w]|$)/, '\u001b[32m$1\u001b[39m$2'); + } + + // Return source line with coloured char at `position` + return [head, tail].join(''); +} + +function extractErrorMessage(stack) { + if (!stack) return ''; + const m = stack.match(/^\w+: ([^\n]+)/); + return m ? m[1] : stack; +} + +function convertResultToError(result) { + const { className, description } = result; + const err = new Error(extractErrorMessage(description)); + err.stack = description; + Object.defineProperty(err, 'name', { value: className }); + return err; +} + +class RemoteObject { + constructor(attributes) { + Object.assign(this, attributes); + if (this.type === 'number') { + this.value = + this.unserializableValue ? +this.unserializableValue : +this.value; + } + } + + [util.inspect.custom](depth, opts) { + function formatProperty(prop) { + switch (prop.type) { + case 'string': + case 'undefined': + return util.inspect(prop.value, opts); + + case 'number': + case 'boolean': + return opts.stylize(prop.value, prop.type); + + case 'object': + case 'symbol': + if (prop.subtype === 'date') { + return util.inspect(new Date(prop.value), opts); + } + if (prop.subtype === 'array') { + return opts.stylize(prop.value, 'special'); + } + return opts.stylize(prop.value, prop.subtype || 'special'); + + default: + return prop.value; + } + } + switch (this.type) { + case 'boolean': + case 'number': + case 'string': + case 'undefined': + return util.inspect(this.value, opts); + + case 'symbol': + return opts.stylize(this.description, 'special'); + + case 'function': { + const fnName = extractFunctionName(this.description); + const formatted = `[${this.className}${fnName}]`; + return opts.stylize(formatted, 'special'); + } + + case 'object': + switch (this.subtype) { + case 'date': + return util.inspect(new Date(this.description), opts); + + case 'null': + return util.inspect(null, opts); + + case 'regexp': + return opts.stylize(this.description, 'regexp'); + + default: + break; + } + if (this.preview) { + const props = this.preview.properties + .map((prop, idx) => { + const value = formatProperty(prop); + if (prop.name === `${idx}`) return value; + return `${prop.name}: ${value}`; + }); + if (this.preview.overflow) { + props.push('...'); + } + const singleLine = props.join(', '); + const propString = + singleLine.length > 60 ? props.join(',\n ') : singleLine; + + return this.subtype === 'array' ? + `[ ${propString} ]` : `{ ${propString} }`; + } + return this.description; + + default: + return this.description; + } + } + + static fromEvalResult({ result, wasThrown }) { + if (wasThrown) return convertResultToError(result); + return new RemoteObject(result); + } +} + +class ScopeSnapshot { + constructor(scope, properties) { + Object.assign(this, scope); + this.properties = new Map(properties.map((prop) => { + const value = new RemoteObject(prop.value); + return [prop.name, value]; + })); + this.completionGroup = properties.map((prop) => prop.name); + } + + [util.inspect.custom](depth, opts) { + const type = `${this.type[0].toUpperCase()}${this.type.slice(1)}`; + const name = this.name ? `<${this.name}>` : ''; + const prefix = `${type}${name} `; + return util.inspect(this.properties, opts) + .replace(/^Map /, prefix); + } +} + +function copyOwnProperties(target, source) { + Object.getOwnPropertyNames(source).forEach((prop) => { + const descriptor = Object.getOwnPropertyDescriptor(source, prop); + Object.defineProperty(target, prop, descriptor); + }); +} + +function aliasProperties(target, mapping) { + Object.keys(mapping).forEach((key) => { + const descriptor = Object.getOwnPropertyDescriptor(target, key); + Object.defineProperty(target, mapping[key], descriptor); + }); +} + +function createRepl(inspector) { + const { Debugger, HeapProfiler, Profiler, Runtime } = inspector; + + let repl; + + // Things we want to keep around + const history = { control: [], debug: [] }; + const watchedExpressions = []; + const knownBreakpoints = []; + let pauseOnExceptionState = 'none'; + let lastCommand; + + // Things we need to reset when the app restarts + let knownScripts; + let currentBacktrace; + let selectedFrame; + let exitDebugRepl; + + function resetOnStart() { + knownScripts = {}; + currentBacktrace = null; + selectedFrame = null; + + if (exitDebugRepl) exitDebugRepl(); + exitDebugRepl = null; + } + resetOnStart(); + + const INSPECT_OPTIONS = { colors: inspector.stdout.isTTY }; + function inspect(value) { + return util.inspect(value, INSPECT_OPTIONS); + } + + function print(value, oneline = false) { + const text = typeof value === 'string' ? value : inspect(value); + return inspector.print(text, oneline); + } + + function getCurrentLocation() { + if (!selectedFrame) { + throw new Error('Requires execution to be paused'); + } + return selectedFrame.location; + } + + function isCurrentScript(script) { + return selectedFrame && getCurrentLocation().scriptId === script.scriptId; + } + + function formatScripts(displayNatives = false) { + function isVisible(script) { + if (displayNatives) return true; + return !script.isNative || isCurrentScript(script); + } + + return Object.keys(knownScripts) + .map((scriptId) => knownScripts[scriptId]) + .filter(isVisible) + .map((script) => { + const isCurrent = isCurrentScript(script); + const { isNative, url } = script; + const name = `${getRelativePath(url)}${isNative ? ' ' : ''}`; + return `${isCurrent ? '*' : ' '} ${script.scriptId}: ${name}`; + }) + .join('\n'); + } + function listScripts(displayNatives = false) { + print(formatScripts(displayNatives)); + } + listScripts[util.inspect.custom] = function listWithoutInternal() { + return formatScripts(); + }; + + const profiles = []; + class Profile { + constructor(data) { + this.data = data; + } + + static createAndRegister({ profile }) { + const p = new Profile(profile); + profiles.push(p); + return p; + } + + [util.inspect.custom](depth, { stylize }) { + const { startTime, endTime } = this.data; + return stylize(`[Profile ${endTime - startTime}μs]`, 'special'); + } + + save(filename = 'node.cpuprofile') { + const absoluteFile = Path.resolve(filename); + const json = JSON.stringify(this.data); + FS.writeFileSync(absoluteFile, json); + print('Saved profile to ' + absoluteFile); + } + } + + class SourceSnippet { + constructor(location, delta, scriptSource) { + Object.assign(this, location); + this.scriptSource = scriptSource; + this.delta = delta; + } + + [util.inspect.custom](depth, options) { + const { scriptId, lineNumber, columnNumber, delta, scriptSource } = this; + const start = Math.max(1, lineNumber - delta + 1); + const end = lineNumber + delta + 1; + + const lines = scriptSource.split('\n'); + return lines.slice(start - 1, end).map((lineText, offset) => { + const i = start + offset; + const isCurrent = i === (lineNumber + 1); + + const markedLine = isCurrent + ? markSourceColumn(lineText, columnNumber, options.colors) + : lineText; + + let isBreakpoint = false; + knownBreakpoints.forEach(({ location }) => { + if (!location) return; + if (scriptId === location.scriptId && + i === (location.lineNumber + 1)) { + isBreakpoint = true; + } + }); + + let prefixChar = ' '; + if (isCurrent) { + prefixChar = '>'; + } else if (isBreakpoint) { + prefixChar = '*'; + } + return `${leftPad(i, prefixChar, end)} ${markedLine}`; + }).join('\n'); + } + } + + function getSourceSnippet(location, delta = 5) { + const { scriptId } = location; + return Debugger.getScriptSource({ scriptId }) + .then(({ scriptSource }) => + new SourceSnippet(location, delta, scriptSource)); + } + + class CallFrame { + constructor(callFrame) { + Object.assign(this, callFrame); + } + + loadScopes() { + return Promise.all( + this.scopeChain + .filter((scope) => scope.type !== 'global') + .map((scope) => { + const { objectId } = scope.object; + return Runtime.getProperties({ + objectId, + generatePreview: true, + }).then(({ result }) => new ScopeSnapshot(scope, result)); + }) + ); + } + + list(delta = 5) { + return getSourceSnippet(this.location, delta); + } + } + + class Backtrace extends Array { + [util.inspect.custom]() { + return this.map((callFrame, idx) => { + const { + location: { scriptId, lineNumber, columnNumber }, + functionName + } = callFrame; + const name = functionName || '(anonymous)'; + + const script = knownScripts[scriptId]; + const relativeUrl = + (script && getRelativePath(script.url)) || ''; + const frameLocation = + `${relativeUrl}:${lineNumber + 1}:${columnNumber}`; + + return `#${idx} ${name} ${frameLocation}`; + }).join('\n'); + } + + static from(callFrames) { + return super.from(Array.from(callFrames).map((callFrame) => { + if (callFrame instanceof CallFrame) { + return callFrame; + } + return new CallFrame(callFrame); + })); + } + } + + function prepareControlCode(input) { + if (input === '\n') return lastCommand; + // exec process.title => exec("process.title"); + const match = input.match(/^\s*exec\s+([^\n]*)/); + if (match) { + lastCommand = `exec(${JSON.stringify(match[1])})`; + } else { + lastCommand = input; + } + return lastCommand; + } + + function evalInCurrentContext(code) { + // Repl asked for scope variables + if (code === '.scope') { + if (!selectedFrame) { + return Promise.reject(new Error('Requires execution to be paused')); + } + return selectedFrame.loadScopes().then((scopes) => { + return scopes.map((scope) => scope.completionGroup); + }); + } + + if (selectedFrame) { + return Debugger.evaluateOnCallFrame({ + callFrameId: selectedFrame.callFrameId, + expression: code, + objectGroup: 'node-inspect', + generatePreview: true, + }).then(RemoteObject.fromEvalResult); + } + return Runtime.evaluate({ + expression: code, + objectGroup: 'node-inspect', + generatePreview: true, + }).then(RemoteObject.fromEvalResult); + } + + function controlEval(input, context, filename, callback) { + debuglog('eval:', input); + function returnToCallback(error, result) { + debuglog('end-eval:', input, error); + callback(error, result); + } + + try { + const code = prepareControlCode(input); + const result = vm.runInContext(code, context, filename); + + if (result && typeof result.then === 'function') { + toCallback(result, returnToCallback); + return; + } + returnToCallback(null, result); + } catch (e) { + returnToCallback(e); + } + } + + function debugEval(input, context, filename, callback) { + debuglog('eval:', input); + function returnToCallback(error, result) { + debuglog('end-eval:', input, error); + callback(error, result); + } + + try { + const result = evalInCurrentContext(input); + + if (result && typeof result.then === 'function') { + toCallback(result, returnToCallback); + return; + } + returnToCallback(null, result); + } catch (e) { + returnToCallback(e); + } + } + + function formatWatchers(verbose = false) { + if (!watchedExpressions.length) { + return Promise.resolve(''); + } + + const inspectValue = (expr) => + evalInCurrentContext(expr) + // .then(formatValue) + .catch((error) => `<${error.message}>`); + const lastIndex = watchedExpressions.length - 1; + + return Promise.all(watchedExpressions.map(inspectValue)) + .then((values) => { + const lines = watchedExpressions + .map((expr, idx) => { + const prefix = `${leftPad(idx, ' ', lastIndex)}: ${expr} =`; + const value = inspect(values[idx], { colors: true }); + if (value.indexOf('\n') === -1) { + return `${prefix} ${value}`; + } + return `${prefix}\n ${value.split('\n').join('\n ')}`; + }); + return lines.join('\n'); + }) + .then((valueList) => { + return verbose ? `Watchers:\n${valueList}\n` : valueList; + }); + } + + function watchers(verbose = false) { + return formatWatchers(verbose).then(print); + } + + // List source code + function list(delta = 5) { + return selectedFrame.list(delta) + .then(null, (error) => { + print('You can\'t list source code right now'); + throw error; + }); + } + + function handleBreakpointResolved({ breakpointId, location }) { + const script = knownScripts[location.scriptId]; + const scriptUrl = script && script.url; + if (scriptUrl) { + Object.assign(location, { scriptUrl }); + } + const isExisting = knownBreakpoints.some((bp) => { + if (bp.breakpointId === breakpointId) { + Object.assign(bp, { location }); + return true; + } + return false; + }); + if (!isExisting) { + knownBreakpoints.push({ breakpointId, location }); + } + } + + function listBreakpoints() { + if (!knownBreakpoints.length) { + print('No breakpoints yet'); + return; + } + + function formatLocation(location) { + if (!location) return ''; + const script = knownScripts[location.scriptId]; + const scriptUrl = script ? script.url : location.scriptUrl; + return `${getRelativePath(scriptUrl)}:${location.lineNumber + 1}`; + } + const breaklist = knownBreakpoints + .map((bp, idx) => `#${idx} ${formatLocation(bp.location)}`) + .join('\n'); + print(breaklist); + } + + function setBreakpoint(script, line, condition, silent) { + function registerBreakpoint({ breakpointId, actualLocation }) { + handleBreakpointResolved({ breakpointId, location: actualLocation }); + if (actualLocation && actualLocation.scriptId) { + if (!silent) return getSourceSnippet(actualLocation, 5); + } else { + print(`Warning: script '${script}' was not loaded yet.`); + } + return undefined; + } + + // setBreakpoint(): set breakpoint at current location + if (script === undefined) { + return Debugger + .setBreakpoint({ location: getCurrentLocation(), condition }) + .then(registerBreakpoint); + } + + // setBreakpoint(line): set breakpoint in current script at specific line + if (line === undefined && typeof script === 'number') { + const location = { + scriptId: getCurrentLocation().scriptId, + lineNumber: script - 1, + }; + return Debugger.setBreakpoint({ location, condition }) + .then(registerBreakpoint); + } + + if (typeof script !== 'string') { + throw new TypeError(`setBreakpoint() expects a string, got ${script}`); + } + + // setBreakpoint('fn()'): Break when a function is called + if (script.endsWith('()')) { + const debugExpr = `debug(${script.slice(0, -2)})`; + const debugCall = selectedFrame + ? Debugger.evaluateOnCallFrame({ + callFrameId: selectedFrame.callFrameId, + expression: debugExpr, + includeCommandLineAPI: true, + }) + : Runtime.evaluate({ + expression: debugExpr, + includeCommandLineAPI: true, + }); + return debugCall.then(({ result, wasThrown }) => { + if (wasThrown) return convertResultToError(result); + return undefined; // This breakpoint can't be removed the same way + }); + } + + // setBreakpoint('scriptname') + let scriptId = null; + let ambiguous = false; + if (knownScripts[script]) { + scriptId = script; + } else { + for (const id of Object.keys(knownScripts)) { + const scriptUrl = knownScripts[id].url; + if (scriptUrl && scriptUrl.indexOf(script) !== -1) { + if (scriptId !== null) { + ambiguous = true; + } + scriptId = id; + } + } + } + + if (ambiguous) { + print('Script name is ambiguous'); + return undefined; + } + if (line <= 0) { + print('Line should be a positive value'); + return undefined; + } + + if (scriptId !== null) { + const location = { scriptId, lineNumber: line - 1 }; + return Debugger.setBreakpoint({ location, condition }) + .then(registerBreakpoint); + } + + const escapedPath = script.replace(/([/\\.?*()^${}|[\]])/g, '\\$1'); + const urlRegex = `^(.*[\\/\\\\])?${escapedPath}$`; + + return Debugger + .setBreakpointByUrl({ urlRegex, lineNumber: line - 1, condition }) + .then((bp) => { + // TODO: handle bp.locations in case the regex matches existing files + if (!bp.location) { // Fake it for now. + Object.assign(bp, { + actualLocation: { + scriptUrl: `.*/${script}$`, + lineNumber: line - 1, + }, + }); + } + return registerBreakpoint(bp); + }); + } + + function clearBreakpoint(url, line) { + const breakpoint = knownBreakpoints.find(({ location }) => { + if (!location) return false; + const script = knownScripts[location.scriptId]; + if (!script) return false; + return ( + script.url.indexOf(url) !== -1 && (location.lineNumber + 1) === line + ); + }); + if (!breakpoint) { + print(`Could not find breakpoint at ${url}:${line}`); + return Promise.resolve(); + } + return Debugger.removeBreakpoint({ breakpointId: breakpoint.breakpointId }) + .then(() => { + const idx = knownBreakpoints.indexOf(breakpoint); + knownBreakpoints.splice(idx, 1); + }); + } + + function restoreBreakpoints() { + const lastBreakpoints = knownBreakpoints.slice(); + knownBreakpoints.length = 0; + const newBreakpoints = lastBreakpoints + .filter(({ location }) => !!location.scriptUrl) + .map(({ location }) => + setBreakpoint(location.scriptUrl, location.lineNumber + 1)); + if (!newBreakpoints.length) return Promise.resolve(); + return Promise.all(newBreakpoints).then((results) => { + print(`${results.length} breakpoints restored.`); + }); + } + + function setPauseOnExceptions(state) { + return Debugger.setPauseOnExceptions({ state }) + .then(() => { + pauseOnExceptionState = state; + }); + } + + Debugger.on('paused', ({ callFrames, reason /* , hitBreakpoints */ }) => { + if (process.env.NODE_INSPECT_RESUME_ON_START === '1' && + reason === 'Break on start') { + debuglog('Paused on start, but NODE_INSPECT_RESUME_ON_START' + + ' environment variable is set to 1, resuming'); + inspector.client.callMethod('Debugger.resume'); + return; + } + + // Save execution context's data + currentBacktrace = Backtrace.from(callFrames); + selectedFrame = currentBacktrace[0]; + const { scriptId, lineNumber } = selectedFrame.location; + + const breakType = reason === 'other' ? 'break' : reason; + const script = knownScripts[scriptId]; + const scriptUrl = script ? getRelativePath(script.url) : '[unknown]'; + + const header = `${breakType} in ${scriptUrl}:${lineNumber + 1}`; + + inspector.suspendReplWhile(() => + Promise.all([formatWatchers(true), selectedFrame.list(2)]) + .then(([watcherList, context]) => { + if (watcherList) { + return `${watcherList}\n${inspect(context)}`; + } + return inspect(context); + }).then((breakContext) => { + print(`${header}\n${breakContext}`); + })); + }); + + function handleResumed() { + currentBacktrace = null; + selectedFrame = null; + } + + Debugger.on('resumed', handleResumed); + + Debugger.on('breakpointResolved', handleBreakpointResolved); + + Debugger.on('scriptParsed', (script) => { + const { scriptId, url } = script; + if (url) { + knownScripts[scriptId] = Object.assign({ + isNative: isNativeUrl(url), + }, script); + } + }); + + Profiler.on('consoleProfileFinished', ({ profile }) => { + Profile.createAndRegister({ profile }); + print([ + 'Captured new CPU profile.', + `Access it with profiles[${profiles.length - 1}]` + ].join('\n')); + }); + + function initializeContext(context) { + inspector.domainNames.forEach((domain) => { + Object.defineProperty(context, domain, { + value: inspector[domain], + enumerable: true, + configurable: true, + writeable: false, + }); + }); + + copyOwnProperties(context, { + get help() { + print(HELP); + }, + + get run() { + return inspector.run(); + }, + + get kill() { + return inspector.killChild(); + }, + + get restart() { + return inspector.run(); + }, + + get cont() { + handleResumed(); + return Debugger.resume(); + }, + + get next() { + handleResumed(); + return Debugger.stepOver(); + }, + + get step() { + handleResumed(); + return Debugger.stepInto(); + }, + + get out() { + handleResumed(); + return Debugger.stepOut(); + }, + + get pause() { + return Debugger.pause(); + }, + + get backtrace() { + return currentBacktrace; + }, + + get breakpoints() { + return listBreakpoints(); + }, + + exec(expr) { + return evalInCurrentContext(expr); + }, + + get profile() { + return Profiler.start(); + }, + + get profileEnd() { + return Profiler.stop() + .then(Profile.createAndRegister); + }, + + get profiles() { + return profiles; + }, + + takeHeapSnapshot(filename = 'node.heapsnapshot') { + return new Promise((resolve, reject) => { + const absoluteFile = Path.resolve(filename); + const writer = FS.createWriteStream(absoluteFile); + let sizeWritten = 0; + function onProgress({ done, total, finished }) { + if (finished) { + print('Heap snaphost prepared.'); + } else { + print(`Heap snapshot: ${done}/${total}`, true); + } + } + function onChunk({ chunk }) { + sizeWritten += chunk.length; + writer.write(chunk); + print(`Writing snapshot: ${sizeWritten}`, true); + } + function onResolve() { + writer.end(() => { + teardown(); + print(`Wrote snapshot: ${absoluteFile}`); + resolve(); + }); + } + function onReject(error) { + teardown(); + reject(error); + } + function teardown() { + HeapProfiler.removeListener( + 'reportHeapSnapshotProgress', onProgress); + HeapProfiler.removeListener('addHeapSnapshotChunk', onChunk); + } + + HeapProfiler.on('reportHeapSnapshotProgress', onProgress); + HeapProfiler.on('addHeapSnapshotChunk', onChunk); + + print('Heap snapshot: 0/0', true); + HeapProfiler.takeHeapSnapshot({ reportProgress: true }) + .then(onResolve, onReject); + }); + }, + + get watchers() { + return watchers(); + }, + + watch(expr) { + watchedExpressions.push(expr); + }, + + unwatch(expr) { + const index = watchedExpressions.indexOf(expr); + + // Unwatch by expression + // or + // Unwatch by watcher number + watchedExpressions.splice(index !== -1 ? index : +expr, 1); + }, + + get repl() { + // Don't display any default messages + const listeners = repl.listeners('SIGINT').slice(0); + repl.removeAllListeners('SIGINT'); + + const oldContext = repl.context; + + exitDebugRepl = () => { + // Restore all listeners + process.nextTick(() => { + listeners.forEach((listener) => { + repl.on('SIGINT', listener); + }); + }); + + // Exit debug repl + repl.eval = controlEval; + + // Swap history + history.debug = repl.history; + repl.history = history.control; + + repl.context = oldContext; + repl.setPrompt('debug> '); + repl.displayPrompt(); + + repl.removeListener('SIGINT', exitDebugRepl); + repl.removeListener('exit', exitDebugRepl); + + exitDebugRepl = null; + }; + + // Exit debug repl on SIGINT + repl.on('SIGINT', exitDebugRepl); + + // Exit debug repl on repl exit + repl.on('exit', exitDebugRepl); + + // Set new + repl.eval = debugEval; + repl.context = {}; + + // Swap history + history.control = repl.history; + repl.history = history.debug; + + repl.setPrompt('> '); + + print('Press Ctrl + C to leave debug repl'); + repl.displayPrompt(); + }, + + get version() { + return Runtime.evaluate({ + expression: 'process.versions.v8', + contextId: 1, + returnByValue: true, + }).then(({ result }) => { + print(result.value); + }); + }, + + scripts: listScripts, + + setBreakpoint, + clearBreakpoint, + setPauseOnExceptions, + get breakOnException() { + return setPauseOnExceptions('all'); + }, + get breakOnUncaught() { + return setPauseOnExceptions('uncaught'); + }, + get breakOnNone() { + return setPauseOnExceptions('none'); + }, + + list, + }); + aliasProperties(context, SHORTCUTS); + } + + function initAfterStart() { + const setupTasks = [ + Runtime.enable(), + Profiler.enable(), + Profiler.setSamplingInterval({ interval: 100 }), + Debugger.enable(), + Debugger.setPauseOnExceptions({ state: 'none' }), + Debugger.setAsyncCallStackDepth({ maxDepth: 0 }), + Debugger.setBlackboxPatterns({ patterns: [] }), + Debugger.setPauseOnExceptions({ state: pauseOnExceptionState }), + restoreBreakpoints(), + Runtime.runIfWaitingForDebugger(), + ]; + return Promise.all(setupTasks); + } + + return function startRepl() { + inspector.client.on('close', () => { + resetOnStart(); + }); + inspector.client.on('ready', () => { + initAfterStart(); + }); + + const replOptions = { + prompt: 'debug> ', + input: inspector.stdin, + output: inspector.stdout, + eval: controlEval, + useGlobal: false, + ignoreUndefined: true, + }; + + repl = Repl.start(replOptions); + initializeContext(repl.context); + repl.on('reset', initializeContext); + + repl.defineCommand('interrupt', () => { + // We want this for testing purposes where sending CTRL-C can be tricky. + repl.emit('SIGINT'); + }); + + // Init once for the initial connection + initAfterStart(); + + return repl; + }; +} +module.exports = createRepl; diff --git a/lib/internal/main/inspect.js b/lib/internal/main/inspect.js index 4873683048cc79..d9dab0dc92b118 100644 --- a/lib/internal/main/inspect.js +++ b/lib/internal/main/inspect.js @@ -13,5 +13,5 @@ markBootstrapComplete(); // Start the debugger agent. process.nextTick(() => { - require('internal/deps/node-inspect/lib/_inspect').start(); + require('internal/inspector/_inspect').start(); }); diff --git a/node.gyp b/node.gyp index acae34fa393339..54ede7fe785ad1 100644 --- a/node.gyp +++ b/node.gyp @@ -166,6 +166,9 @@ 'lib/internal/heap_utils.js', 'lib/internal/histogram.js', 'lib/internal/idna.js', + 'lib/internal/inspector/_inspect.js', + 'lib/internal/inspector/inspect_client.js', + 'lib/internal/inspector/inspect_repl.js', 'lib/internal/inspector_async_hook.js', 'lib/internal/js_stream_socket.js', 'lib/internal/legacy/processbinding.js', @@ -277,9 +280,6 @@ 'deps/v8/tools/tickprocessor.mjs', 'deps/v8/tools/sourcemap.mjs', 'deps/v8/tools/tickprocessor-driver.mjs', - 'deps/node-inspect/lib/_inspect.js', - 'deps/node-inspect/lib/internal/inspect_client.js', - 'deps/node-inspect/lib/internal/inspect_repl.js', 'deps/acorn/acorn/dist/acorn.js', 'deps/acorn/acorn-walk/dist/walk.js', 'deps/acorn-plugins/acorn-class-fields/index.js', From 7ed39693fb5ffbb5e63bd06011f959e64f76def2 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 8 Apr 2021 03:51:24 -0700 Subject: [PATCH 02/12] tools: remove node-inspect from license MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- LICENSE | 23 ----------------------- tools/license-builder.sh | 3 --- 2 files changed, 26 deletions(-) diff --git a/LICENSE b/LICENSE index c6c4f14e2dc193..41f9bc05611a82 100644 --- a/LICENSE +++ b/LICENSE @@ -1333,29 +1333,6 @@ The externally maintained libraries used by Node.js are: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -- node-inspect, located at deps/node-inspect, is licensed as follows: - """ - Copyright Node.js contributors. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to - deal in the Software without restriction, including without limitation the - rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - IN THE SOFTWARE. - """ - - large_pages, located at src/large_pages, is licensed as follows: """ Copyright (C) 2018 Intel Corporation diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 05a15471c7e77f..80aaac11b1cade 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -89,9 +89,6 @@ addlicense "gtest" "test/cctest/gtest" "$(cat "${rootdir}"/test/cctest/gtest/LIC # nghttp2 addlicense "nghttp2" "deps/nghttp2" "$(cat "${rootdir}"/deps/nghttp2/COPYING)" -# node-inspect -addlicense "node-inspect" "deps/node-inspect" "$(cat "${rootdir}"/deps/node-inspect/LICENSE)" - # large_pages addlicense "large_pages" "src/large_pages" "$(sed -e '/SPDX-License-Identifier/,$d' -e 's/^\/\///' "${rootdir}"/src/large_pages/node_large_page.h)" From 21dc0b0b2212915847e6a851bf6d02323711ddcb Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 8 Apr 2021 04:05:12 -0700 Subject: [PATCH 03/12] debugger: remove unused code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove code that was for when `node-inspect` was called as a standalone process. PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- lib/internal/inspector/_inspect.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/internal/inspector/_inspect.js b/lib/internal/inspector/_inspect.js index df4e53979db8fd..8bc1f13e5726f0 100644 --- a/lib/internal/inspector/_inspect.js +++ b/lib/internal/inspector/_inspect.js @@ -29,16 +29,7 @@ const { EventEmitter } = require('events'); const net = require('net'); const util = require('util'); -const runAsStandalone = typeof __dirname !== 'undefined'; - const { 0: InspectClient, 1: createRepl } = - runAsStandalone ? - // This copy of node-inspect is on-disk, relative paths make sense. - [ - require('./inspect_client'), - require('./inspect_repl'), - ] : - // This copy of node-inspect is built into the node executable. [ require('internal/inspector/inspect_client'), require('internal/inspector/inspect_repl'), @@ -336,9 +327,7 @@ function startInspect(argv = process.argv.slice(2), stdin = process.stdin, stdout = process.stdout) { if (argv.length < 1) { - const invokedAs = runAsStandalone ? - 'node-inspect' : - `${process.argv0} ${process.argv[1]}`; + const invokedAs = `${process.argv0} ${process.argv[1]}`; console.error(`Usage: ${invokedAs} script.js`); console.error(` ${invokedAs} :`); From b0713b4186194aa6c38effa2ac632acf85438685 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 20 Apr 2021 07:17:28 -0700 Subject: [PATCH 04/12] debugger: fix inspect restart on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/37224 PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- lib/internal/inspector/inspect_client.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/inspector/inspect_client.js b/lib/internal/inspector/inspect_client.js index 6010f4ac09389d..831a7fda1cdff1 100644 --- a/lib/internal/inspector/inspect_client.js +++ b/lib/internal/inspector/inspect_client.js @@ -228,6 +228,9 @@ class Client extends EventEmitter { if (this._http) { this._http.destroy(); } + if (this._socket) { + this._socket.destroy(); + } this._http = null; this._lastId = 0; this._socket = null; From 807ede70fa52a88604e939dbffbb7b09daeb0beb Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 20 Apr 2021 22:46:42 -0700 Subject: [PATCH 05/12] debugger: accommodate line chunking in Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- lib/internal/inspector/_inspect.js | 43 ++++++++++++++++---------- lib/internal/inspector/inspect_repl.js | 10 +++--- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/internal/inspector/_inspect.js b/lib/internal/inspector/_inspect.js index 8bc1f13e5726f0..b58b4b695e8505 100644 --- a/lib/internal/inspector/_inspect.js +++ b/lib/internal/inspector/_inspect.js @@ -97,8 +97,8 @@ function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) { const child = spawn(process.execPath, args); child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8'); - child.stdout.on('data', childPrint); - child.stderr.on('data', childPrint); + child.stdout.on('data', (chunk) => childPrint(chunk, 'stdout')); + child.stderr.on('data', (chunk) => childPrint(chunk, 'stderr')); let output = ''; function waitForListenHint(text) { @@ -231,7 +231,7 @@ class NodeInspector { return this.client.connect(port, host) .then(() => { debuglog('connection established'); - this.stdout.write(' ok'); + this.stdout.write(' ok\n'); }, (error) => { debuglog('connect failed', error); // If it's failed to connect 10 times then print failed message @@ -245,7 +245,7 @@ class NodeInspector { }); }; - this.print(`connecting to ${host}:${port} ..`, true); + this.print(`connecting to ${host}:${port} ..`, false); return attemptConnect(); }); } @@ -259,23 +259,32 @@ class NodeInspector { } } - print(text, oneline = false) { + print(text, appendNewline = false) { this.clearLine(); - this.stdout.write(oneline ? text : `${text}\n`); + this.stdout.write(appendNewline ? `${text}\n` : text); } - childPrint(text) { - this.print( - text.toString() - .split(/\r\n|\r|\n/g) - .filter((chunk) => !!chunk) - .map((chunk) => `< ${chunk}`) - .join('\n') - ); - if (!this.paused) { - this.repl.displayPrompt(true); + #stdioBuffers = {stdout: '', stderr: ''}; + childPrint(text, which) { + const lines = (this.#stdioBuffers[which] + text) + .split(/\r\n|\r|\n/g); + + this.#stdioBuffers[which] = ''; + + if (lines[lines.length - 1] !== '') { + this.#stdioBuffers[which] = lines.pop(); + } + + const textToPrint = lines.map((chunk) => `< ${chunk}`).join('\n'); + + if (lines.length) { + this.print(textToPrint, true); + if (!this.paused) { + this.repl.displayPrompt(true); + } } - if (/Waiting for the debugger to disconnect\.\.\.\n$/.test(text)) { + + if (textToPrint.endsWith('Waiting for the debugger to disconnect...\n')) { this.killChild(); } } diff --git a/lib/internal/inspector/inspect_repl.js b/lib/internal/inspector/inspect_repl.js index 7d5b019e28506d..7fd9772eef733b 100644 --- a/lib/internal/inspector/inspect_repl.js +++ b/lib/internal/inspector/inspect_repl.js @@ -320,9 +320,9 @@ function createRepl(inspector) { return util.inspect(value, INSPECT_OPTIONS); } - function print(value, oneline = false) { + function print(value, addNewline = true) { const text = typeof value === 'string' ? value : inspect(value); - return inspector.print(text, oneline); + return inspector.print(text, addNewline); } function getCurrentLocation() { @@ -928,13 +928,13 @@ function createRepl(inspector) { if (finished) { print('Heap snaphost prepared.'); } else { - print(`Heap snapshot: ${done}/${total}`, true); + print(`Heap snapshot: ${done}/${total}`, false); } } function onChunk({ chunk }) { sizeWritten += chunk.length; writer.write(chunk); - print(`Writing snapshot: ${sizeWritten}`, true); + print(`Writing snapshot: ${sizeWritten}`, false); } function onResolve() { writer.end(() => { @@ -956,7 +956,7 @@ function createRepl(inspector) { HeapProfiler.on('reportHeapSnapshotProgress', onProgress); HeapProfiler.on('addHeapSnapshotChunk', onChunk); - print('Heap snapshot: 0/0', true); + print('Heap snapshot: 0/0', false); HeapProfiler.takeHeapSnapshot({ reportProgress: true }) .then(onResolve, onReject); }); From c4c9614e9611c3693c2a86c4231c929e2bc49490 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 21 Apr 2021 16:22:36 -0700 Subject: [PATCH 06/12] debugger: allow longer time to connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make five attempts with a timeout of 1 second each rather than 10 attempts with a timeout of 500ms each. This is to allow for slower-connecting devices like Raspberry Pi. PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- lib/internal/inspector/_inspect.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/inspector/_inspect.js b/lib/internal/inspector/_inspect.js index b58b4b695e8505..b326a772fd39c9 100644 --- a/lib/internal/inspector/_inspect.js +++ b/lib/internal/inspector/_inspect.js @@ -234,13 +234,13 @@ class NodeInspector { this.stdout.write(' ok\n'); }, (error) => { debuglog('connect failed', error); - // If it's failed to connect 10 times then print failed message - if (connectionAttempts >= 10) { + // If it's failed to connect 5 times then print failed message + if (connectionAttempts >= 5) { this.stdout.write(' failed to connect, please retry\n'); process.exit(1); } - return new Promise((resolve) => setTimeout(resolve, 500)) + return new Promise((resolve) => setTimeout(resolve, 1000)) .then(attemptConnect); }); }; From c6b4ab0d9f7249d75da1473a1c962e3b2e5d0b74 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 22 Apr 2021 10:40:12 -0700 Subject: [PATCH 07/12] test: fix test-inspector-cli-address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was assuming that the entire string being sought would arrive in a single data chunk, but it can be split across multiple chunks. PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- deps/node-inspect/test/cli/address.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deps/node-inspect/test/cli/address.test.js b/deps/node-inspect/test/cli/address.test.js index 1dbe4f37b42175..b55a2d078ea4a4 100644 --- a/deps/node-inspect/test/cli/address.test.js +++ b/deps/node-inspect/test/cli/address.test.js @@ -16,7 +16,9 @@ function launchTarget(...args) { }; childProc.on('exit', onExit); childProc.stderr.setEncoding('utf8'); - childProc.stderr.on('data', (data) => { + let data = ''; + childProc.stderr.on('data', (chunk) => { + data += chunk; const ret = kDebuggerMsgReg.exec(data); childProc.removeListener('exit', onExit); if (ret) { From 3ef3501e498c4a05776440700e9238ef15f53c86 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 8 Apr 2021 23:55:45 -0700 Subject: [PATCH 08/12] test,debugger: migrate node-inspect tests to core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the node-inspect tests to core (where node-inspect code now lives) and remove node-inspect from deps directory. PR-URL: https://github.com/nodejs/node/pull/38161 Refs: https://github.com/nodejs/node/discussions/36481 Reviewed-By: Matteo Collina Reviewed-By: Jan Krems Reviewed-By: Colin Ihrig Reviewed-By: Stephen Belanger Reviewed-By: Gerhard Stöbich Reviewed-By: Michaël Zasso --- Makefile | 4 - deps/node-inspect/.editorconfig | 11 - deps/node-inspect/.eslintrc | 147 --- deps/node-inspect/.github/workflows/ci.yml | 31 - deps/node-inspect/.gitignore | 4 - deps/node-inspect/.npmrc | 2 - deps/node-inspect/.travis.yml | 13 - deps/node-inspect/CHANGELOG.md | 296 ----- deps/node-inspect/CONTRIBUTING.md | 181 --- deps/node-inspect/GOVERNANCE.md | 4 - deps/node-inspect/LICENSE | 19 - deps/node-inspect/README.md | 29 - deps/node-inspect/appveyor.yml | 13 - deps/node-inspect/cli.js | 2 - deps/node-inspect/lib/_inspect.js | 371 ------ deps/node-inspect/lib/cli.js | 24 - .../lib/internal/inspect_client.js | 352 ------ .../node-inspect/lib/internal/inspect_repl.js | 1108 ----------------- deps/node-inspect/package.json | 45 - deps/node-inspect/test/cli/backtrace.test.js | 30 - deps/node-inspect/test/cli/break.test.js | 195 --- deps/node-inspect/test/cli/exec.test.js | 77 -- .../test/cli/heap-profiler.test.js | 34 - deps/node-inspect/test/cli/help.test.js | 22 - .../test/cli/invalid-args.test.js | 54 - deps/node-inspect/test/cli/launch.test.js | 196 --- deps/node-inspect/test/cli/pid.test.js | 52 - .../test/cli/preserve-breaks.test.js | 64 - .../eslint-rules/align-function-arguments.js | 76 -- .../align-multiline-assignment.js | 68 - .../assert-fail-single-argument.js | 30 - .../tools/eslint-rules/buffer-constructor.js | 25 - .../tools/eslint-rules/new-with-error.js | 31 - .../eslint-rules/no-let-in-for-declaration.js | 46 - .../eslint-rules/prefer-assert-methods.js | 39 - .../tools/eslint-rules/require-buffer.js | 19 - .../tools/eslint-rules/required-modules.js | 99 -- lib/internal/inspector/inspect_repl.js | 2 +- test/README.md | 1 + test/common/README.md | 29 + .../common/inspector-cli.js | 75 +- .../fixtures/inspector-cli}/alive.js | 0 .../fixtures/inspector-cli}/backtrace.js | 0 .../fixtures/inspector-cli}/break.js | 0 .../fixtures/inspector-cli}/cjs/index.js | 0 .../fixtures/inspector-cli}/cjs/other.js | 0 .../fixtures/inspector-cli}/empty.js | 0 .../fixtures/inspector-cli}/exceptions.js | 0 .../fixtures/inspector-cli}/three-lines.js | 0 .../fixtures/inspector-cli}/use-strict.js | 0 test/inspector-cli/inspector-cli.status | 7 + .../test-inspector-cli-address.js | 28 +- .../test-inspector-cli-auto-resume.js | 36 + .../test-inspector-cli-backtrace.js | 36 + .../inspector-cli/test-inspector-cli-break.js | 131 ++ .../test-inspector-cli-clear-breakpoints.js | 53 + .../test-inspector-cli-custom-port.js | 30 + .../test-inspector-cli-exceptions.js | 44 +- .../test-inspector-cli-exec-scope.js | 38 + test/inspector-cli/test-inspector-cli-exec.js | 67 + .../test-inspector-cli-heap-profiler.js | 37 + test/inspector-cli/test-inspector-cli-help.js | 27 + .../test-inspector-cli-invalid-args.js | 59 + .../test-inspector-cli-launch.js | 47 + .../test-inspector-cli-low-level.js | 21 +- test/inspector-cli/test-inspector-cli-pid.js | 61 + .../test-inspector-cli-preserve-breaks.js | 72 ++ .../test-inspector-cli-profile.js | 22 +- ...ector-cli-random-port-with-inspect-port.js | 30 + .../test-inspector-cli-random-port.js | 30 + ...st-inspector-cli-run-after-quit-restart.js | 90 ++ .../test-inspector-cli-sb-before-load.js | 44 + .../test-inspector-cli-scripts.js | 32 +- .../test-inspector-cli-use-strict.js | 18 +- .../test-inspector-cli-watchers.js | 30 +- test/inspector-cli/testcfg.py | 6 + 76 files changed, 1083 insertions(+), 3933 deletions(-) delete mode 100644 deps/node-inspect/.editorconfig delete mode 100644 deps/node-inspect/.eslintrc delete mode 100644 deps/node-inspect/.github/workflows/ci.yml delete mode 100644 deps/node-inspect/.gitignore delete mode 100644 deps/node-inspect/.npmrc delete mode 100644 deps/node-inspect/.travis.yml delete mode 100644 deps/node-inspect/CHANGELOG.md delete mode 100644 deps/node-inspect/CONTRIBUTING.md delete mode 100644 deps/node-inspect/GOVERNANCE.md delete mode 100644 deps/node-inspect/LICENSE delete mode 100644 deps/node-inspect/README.md delete mode 100644 deps/node-inspect/appveyor.yml delete mode 100755 deps/node-inspect/cli.js delete mode 100644 deps/node-inspect/lib/_inspect.js delete mode 100644 deps/node-inspect/lib/cli.js delete mode 100644 deps/node-inspect/lib/internal/inspect_client.js delete mode 100644 deps/node-inspect/lib/internal/inspect_repl.js delete mode 100644 deps/node-inspect/package.json delete mode 100644 deps/node-inspect/test/cli/backtrace.test.js delete mode 100644 deps/node-inspect/test/cli/break.test.js delete mode 100644 deps/node-inspect/test/cli/exec.test.js delete mode 100644 deps/node-inspect/test/cli/heap-profiler.test.js delete mode 100644 deps/node-inspect/test/cli/help.test.js delete mode 100644 deps/node-inspect/test/cli/invalid-args.test.js delete mode 100644 deps/node-inspect/test/cli/launch.test.js delete mode 100644 deps/node-inspect/test/cli/pid.test.js delete mode 100644 deps/node-inspect/test/cli/preserve-breaks.test.js delete mode 100644 deps/node-inspect/tools/eslint-rules/align-function-arguments.js delete mode 100644 deps/node-inspect/tools/eslint-rules/align-multiline-assignment.js delete mode 100644 deps/node-inspect/tools/eslint-rules/assert-fail-single-argument.js delete mode 100644 deps/node-inspect/tools/eslint-rules/buffer-constructor.js delete mode 100644 deps/node-inspect/tools/eslint-rules/new-with-error.js delete mode 100644 deps/node-inspect/tools/eslint-rules/no-let-in-for-declaration.js delete mode 100644 deps/node-inspect/tools/eslint-rules/prefer-assert-methods.js delete mode 100644 deps/node-inspect/tools/eslint-rules/require-buffer.js delete mode 100644 deps/node-inspect/tools/eslint-rules/required-modules.js rename deps/node-inspect/test/cli/start-cli.js => test/common/inspector-cli.js (67%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/alive.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/backtrace.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/break.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/cjs/index.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/cjs/other.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/empty.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/exceptions.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/three-lines.js (100%) rename {deps/node-inspect/examples => test/fixtures/inspector-cli}/use-strict.js (100%) create mode 100644 test/inspector-cli/inspector-cli.status rename deps/node-inspect/test/cli/address.test.js => test/inspector-cli/test-inspector-cli-address.js (74%) create mode 100644 test/inspector-cli/test-inspector-cli-auto-resume.js create mode 100644 test/inspector-cli/test-inspector-cli-backtrace.js create mode 100644 test/inspector-cli/test-inspector-cli-break.js create mode 100644 test/inspector-cli/test-inspector-cli-clear-breakpoints.js create mode 100644 test/inspector-cli/test-inspector-cli-custom-port.js rename deps/node-inspect/test/cli/exceptions.test.js => test/inspector-cli/test-inspector-cli-exceptions.js (55%) create mode 100644 test/inspector-cli/test-inspector-cli-exec-scope.js create mode 100644 test/inspector-cli/test-inspector-cli-exec.js create mode 100644 test/inspector-cli/test-inspector-cli-heap-profiler.js create mode 100644 test/inspector-cli/test-inspector-cli-help.js create mode 100644 test/inspector-cli/test-inspector-cli-invalid-args.js create mode 100644 test/inspector-cli/test-inspector-cli-launch.js rename deps/node-inspect/test/cli/low-level.test.js => test/inspector-cli/test-inspector-cli-low-level.js (57%) create mode 100644 test/inspector-cli/test-inspector-cli-pid.js create mode 100644 test/inspector-cli/test-inspector-cli-preserve-breaks.js rename deps/node-inspect/test/cli/profile.test.js => test/inspector-cli/test-inspector-cli-profile.js (52%) create mode 100644 test/inspector-cli/test-inspector-cli-random-port-with-inspect-port.js create mode 100644 test/inspector-cli/test-inspector-cli-random-port.js create mode 100644 test/inspector-cli/test-inspector-cli-run-after-quit-restart.js create mode 100644 test/inspector-cli/test-inspector-cli-sb-before-load.js rename deps/node-inspect/test/cli/scripts.test.js => test/inspector-cli/test-inspector-cli-scripts.js (51%) rename deps/node-inspect/test/cli/use-strict.test.js => test/inspector-cli/test-inspector-cli-use-strict.js (54%) rename deps/node-inspect/test/cli/watchers.test.js => test/inspector-cli/test-inspector-cli-watchers.js (56%) create mode 100644 test/inspector-cli/testcfg.py diff --git a/Makefile b/Makefile index 26746d94f4ab8c..50d6795bc34903 100644 --- a/Makefile +++ b/Makefile @@ -565,10 +565,6 @@ test-pummel: all test-internet: all $(PYTHON) tools/test.py $(PARALLEL_ARGS) internet -test-node-inspect: $(NODE_EXE) - USE_EMBEDDED_NODE_INSPECT=1 $(NODE) tools/test-npm-package \ - --install deps/node-inspect test - test-benchmark: | bench-addons-build $(PYTHON) tools/test.py $(PARALLEL_ARGS) benchmark diff --git a/deps/node-inspect/.editorconfig b/deps/node-inspect/.editorconfig deleted file mode 100644 index beffa3084e7a69..00000000000000 --- a/deps/node-inspect/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/deps/node-inspect/.eslintrc b/deps/node-inspect/.eslintrc deleted file mode 100644 index b6d45aa499655b..00000000000000 --- a/deps/node-inspect/.eslintrc +++ /dev/null @@ -1,147 +0,0 @@ -root: true - -env: - node: true - es6: true - -parserOptions: - ecmaVersion: 2017 - -rules: - # Possible Errors - # http://eslint.org/docs/rules/#possible-errors - comma-dangle: [2, only-multiline] - no-control-regex: 2 - no-debugger: 2 - no-dupe-args: 2 - no-dupe-keys: 2 - no-duplicate-case: 2 - no-empty-character-class: 2 - no-ex-assign: 2 - no-extra-boolean-cast: 2 - no-extra-parens: [2, functions] - no-extra-semi: 2 - no-func-assign: 2 - no-invalid-regexp: 2 - no-irregular-whitespace: 2 - no-obj-calls: 2 - no-proto: 2 - no-template-curly-in-string: 2 - no-unexpected-multiline: 2 - no-unreachable: 2 - no-unsafe-negation: 2 - use-isnan: 2 - valid-typeof: 2 - - # Best Practices - # http://eslint.org/docs/rules/#best-practices - dot-location: [2, property] - no-fallthrough: 2 - no-global-assign: 2 - no-multi-spaces: 2 - no-octal: 2 - no-redeclare: 2 - no-self-assign: 2 - no-unused-labels: 2 - no-useless-call: 2 - no-useless-escape: 2 - no-void: 2 - no-with: 2 - - # Strict Mode - # http://eslint.org/docs/rules/#strict-mode - strict: [2, global] - - # Variables - # http://eslint.org/docs/rules/#variables - no-delete-var: 2 - no-undef: 2 - no-unused-vars: [2, {args: none}] - - # Node.js and CommonJS - # http://eslint.org/docs/rules/#nodejs-and-commonjs - no-mixed-requires: 2 - no-new-require: 2 - no-path-concat: 2 - no-restricted-modules: [2, sys, _linklist] - no-restricted-properties: [2, { - object: assert, - property: deepEqual, - message: Please use assert.deepStrictEqual(). - }, { - property: __defineGetter__, - message: __defineGetter__ is deprecated. - }, { - property: __defineSetter__, - message: __defineSetter__ is deprecated. - }] - - # Stylistic Issues - # http://eslint.org/docs/rules/#stylistic-issues - brace-style: [2, 1tbs, {allowSingleLine: true}] - comma-spacing: 2 - comma-style: 2 - computed-property-spacing: 2 - eol-last: 2 - func-call-spacing: 2 - func-name-matching: 2 - indent: [2, 2, {SwitchCase: 1, MemberExpression: 1}] - key-spacing: [2, {mode: minimum}] - keyword-spacing: 2 - linebreak-style: [2, unix] - max-len: [2, 80, 2] - new-parens: 2 - no-mixed-spaces-and-tabs: 2 - no-multiple-empty-lines: [2, {max: 2, maxEOF: 0, maxBOF: 0}] - no-tabs: 2 - no-trailing-spaces: 2 - quotes: [2, single, avoid-escape] - semi: 2 - semi-spacing: 2 - space-before-blocks: [2, always] - space-before-function-paren: [2, never] - space-in-parens: [2, never] - space-infix-ops: 2 - space-unary-ops: 2 - - # ECMAScript 6 - # http://eslint.org/docs/rules/#ecmascript-6 - arrow-parens: [2, always] - arrow-spacing: [2, {before: true, after: true}] - constructor-super: 2 - no-class-assign: 2 - no-confusing-arrow: 2 - no-const-assign: 2 - no-dupe-class-members: 2 - no-new-symbol: 2 - no-this-before-super: 2 - prefer-const: [2, {ignoreReadBeforeAssign: true}] - rest-spread-spacing: 2 - template-curly-spacing: 2 - - # Custom rules in tools/eslint-rules - align-function-arguments: 2 - align-multiline-assignment: 2 - assert-fail-single-argument: 2 - new-with-error: [2, Error, RangeError, TypeError, SyntaxError, ReferenceError] - -# Global scoped method and vars -globals: - COUNTER_HTTP_CLIENT_REQUEST: false - COUNTER_HTTP_CLIENT_RESPONSE: false - COUNTER_HTTP_SERVER_REQUEST: false - COUNTER_HTTP_SERVER_RESPONSE: false - COUNTER_NET_SERVER_CONNECTION: false - COUNTER_NET_SERVER_CONNECTION_CLOSE: false - DTRACE_HTTP_CLIENT_REQUEST: false - DTRACE_HTTP_CLIENT_RESPONSE: false - DTRACE_HTTP_SERVER_REQUEST: false - DTRACE_HTTP_SERVER_RESPONSE: false - DTRACE_NET_SERVER_CONNECTION: false - DTRACE_NET_STREAM_END: false - LTTNG_HTTP_CLIENT_REQUEST: false - LTTNG_HTTP_CLIENT_RESPONSE: false - LTTNG_HTTP_SERVER_REQUEST: false - LTTNG_HTTP_SERVER_RESPONSE: false - LTTNG_NET_SERVER_CONNECTION: false - LTTNG_NET_STREAM_END: false diff --git a/deps/node-inspect/.github/workflows/ci.yml b/deps/node-inspect/.github/workflows/ci.yml deleted file mode 100644 index 968316a34779a5..00000000000000 --- a/deps/node-inspect/.github/workflows/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Node CI - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - node-version: - # See https://github.com/nodejs/node-inspect/pull/78 - # - 10.x - - 12.x - - 13.x - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install, build, and test - run: | - npm install - npm run build --if-present - npm test - env: - CI: true diff --git a/deps/node-inspect/.gitignore b/deps/node-inspect/.gitignore deleted file mode 100644 index 72e2c8c18012a8..00000000000000 --- a/deps/node-inspect/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -npm-debug.log -/tmp -/.vs diff --git a/deps/node-inspect/.npmrc b/deps/node-inspect/.npmrc deleted file mode 100644 index b7c8444fee52a6..00000000000000 --- a/deps/node-inspect/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -registry=https://registry.npmjs.org -package-lock=false diff --git a/deps/node-inspect/.travis.yml b/deps/node-inspect/.travis.yml deleted file mode 100644 index 07418a91eb7986..00000000000000 --- a/deps/node-inspect/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: node_js -node_js: - - '6.8' -before_deploy: - - git config --global user.email "jan.krems@gmail.com" - - git config --global user.name "Jan Krems" -deploy: - provider: script - script: ./node_modules/.bin/nlm release - skip_cleanup: true - 'on': - branch: master - node: '6.8' diff --git a/deps/node-inspect/CHANGELOG.md b/deps/node-inspect/CHANGELOG.md deleted file mode 100644 index 73aeb4df93fe8b..00000000000000 --- a/deps/node-inspect/CHANGELOG.md +++ /dev/null @@ -1,296 +0,0 @@ -### 1.11.6 - -* fix: replace the deprecated "repl.cli" with "repl" - **[@oyyd](https://github.com/oyyd)** [#66](https://github.com/nodejs/node-inspect/pull/66) - - [`5c1d771`](https://github.com/nodejs/node-inspect/commit/5c1d7716523b73e26f98f4f594ee34b7daa920a0) **fix:** replace the deprecated "repl.cli" with "repl" - see: [26260](Refs: https://github.com/nodejs/node/pull/26260) -* Address regressions due to changes in node - **[@jkrems](https://github.com/jkrems)** [#67](https://github.com/nodejs/node-inspect/pull/67) - - [`5b3511e`](https://github.com/nodejs/node-inspect/commit/5b3511ef21d0eba8304d8b2fed33f33aae22f308) **fix:** Address regressions due to changes in node - - -### 1.11.5 - -* Fix eslint issues - **[@jkrems](https://github.com/jkrems)** [#63](https://github.com/nodejs/node-inspect/pull/63) - - [`2adadbc`](https://github.com/nodejs/node-inspect/commit/2adadbc1086d2e374c425acbf96260a122705db2) **style:** Fix eslint issues - - [`a6d2f88`](https://github.com/nodejs/node-inspect/commit/a6d2f882c026409696a1b063ff40ceba7e1ddb86) **doc:** Remove redundant newline at the end - - -### 1.11.4 - -* Handle blocked port - **[@jkrems](https://github.com/jkrems)** [#62](https://github.com/nodejs/node-inspect/pull/62) - - [`3388969`](https://github.com/nodejs/node-inspect/commit/3388969d0032a78ff0cdb8146f170b978ec13b7b) **chore:** Disable package-lock - - [`d278b23`](https://github.com/nodejs/node-inspect/commit/d278b233ae5e11a2b62d01ccbaae594f39b32a96) **fix:** Stop asking to report a blocked port - see: [#60](https://github.com/nodejs/node-inspect/issues/60) - - -### 1.11.3 - -* [`93caa0f`](https://github.com/nodejs/node-inspect/commit/93caa0f5267c7ab452b258d3b03329a0bb5ac7f7) **docs:** Add missing oc in protocol -* [`2d87cbe`](https://github.com/nodejs/node-inspect/commit/2d87cbe76aa968dfc1ac69d9571af1be81abd8e0) **fix:** Make --inspect-port=0 work -* [`ebfd02e`](https://github.com/nodejs/node-inspect/commit/ebfd02ece9b642586023f7791da71defeb13d746) **chore:** Bump tap to 10.7 -* [`c07adb1`](https://github.com/nodejs/node-inspect/commit/c07adb17b164c1cf3da8d38659ea9f5d7ff42e9c) **test:** Use useful break location -* [`94f0bf9`](https://github.com/nodejs/node-inspect/commit/94f0bf97d24c376baf3ecced2088d81715a73464) **fix:** Fix `takeHeapSnapshot()` truncation bug - - -### 1.11.2 - -* [`42e0cd1`](https://github.com/nodejs/node-inspect/commit/42e0cd111d89ed09faba1c0ec45089b0b44de011) **fix:** look for generic hint text - - -### 1.11.1 - -* Prefer --inspect-brk over --debug-brk - **[@ofrobots](https://github.com/ofrobots)** [#43](https://github.com/nodejs/node-inspect/pull/43) - - [`2c1ed27`](https://github.com/nodejs/node-inspect/commit/2c1ed27ee44d9aebb3c5ac50039abae8166a54e3) **fix:** use --inspect-brk with Node 8+ - - -### 1.11.0 - -* doc: add profile and heap to help - **[@joshgav](https://github.com/joshgav)** [#39](https://github.com/nodejs/node-inspect/pull/39) - - [`f64c920`](https://github.com/nodejs/node-inspect/commit/f64c9205bd8382289660aa677d3ac192a9c81fd5) **doc:** add profile and heap to help -* Update test suite to pass on latest nightly - **[@jkrems](https://github.com/jkrems)** [#36](https://github.com/nodejs/node-inspect/pull/36) - - [`41148d7`](https://github.com/nodejs/node-inspect/commit/41148d74a2d563eea3b7ad5463622b6b9fd4c46e) **test:** Remove outdated test - - [`2c224c5`](https://github.com/nodejs/node-inspect/commit/2c224c551619e386e80fc3154cc14562cac063b9) **test:** Accept any kind of "break" - - [`22bf349`](https://github.com/nodejs/node-inspect/commit/22bf349bc86d7bf6fd449791c9d1e7eaf66c2681) **test:** Adjust for v8 5.7 - - [`6ce8c16`](https://github.com/nodejs/node-inspect/commit/6ce8c165c45a491bea8cfb3c67d2ae80e7c34dcb) **test:** Revert to old assertions -* Verify custom port support - **[@jkrems](https://github.com/jkrems)** [#41](https://github.com/nodejs/node-inspect/pull/41) - - [`e3a489f`](https://github.com/nodejs/node-inspect/commit/e3a489f23b089d3d57a25d5efe40daf06de63e23) **test:** custom port -* Support for debugging a pid - **[@jkrems](https://github.com/jkrems)** [#37](https://github.com/nodejs/node-inspect/pull/37) - - [`4179506`](https://github.com/nodejs/node-inspect/commit/4179506a4d546bac2c93b2a7ff491b1fa4494fd9) **feat:** Support for debugging a pid - - -### 1.10.6 - -* chore: Fix usage text for embedded mode - **[@addaleax](https://github.com/addaleax)** [#20](https://github.com/nodejs/node-inspect/pull/20) - - [`b0779f5`](https://github.com/nodejs/node-inspect/commit/b0779f597104e9ada5681f64d7e714525b753511) **chore:** Fix usage text for embedded mode -* print 'ok' after connection - **[@ofrobots](https://github.com/ofrobots)** [#25](https://github.com/nodejs/node-inspect/pull/25) - - [`2a47125`](https://github.com/nodejs/node-inspect/commit/2a4712577f6996fbb078dcfcd7320c397685d76a) **fix:** print 'ok' after connection -* Make autocompletion in REPL work - **[@aqrln](https://github.com/aqrln)** [#28](https://github.com/nodejs/node-inspect/pull/28) - - [`ccab737`](https://github.com/nodejs/node-inspect/commit/ccab737399249a8c2230ed6adfec579c7d724364) **fix:** Make autocompletion in REPL work -* Remove console.error() statement - **[@aqrln](https://github.com/aqrln)** [#30](https://github.com/nodejs/node-inspect/pull/30) - - [`032b045`](https://github.com/nodejs/node-inspect/commit/032b045d4d73622c77b7ebcc26781c6ad98200b3) **style:** Remove console.error() statement -* Take --debug-port into account - **[@jkrems](https://github.com/jkrems)** [#26](https://github.com/nodejs/node-inspect/pull/26) - - [`054d4b1`](https://github.com/nodejs/node-inspect/commit/054d4b10e65f12a3a4b10b4c0ab2a4768cc5e893) **fix:** Take --debug-port into account -* Delay run until breakpoints are restored - **[@jkrems](https://github.com/jkrems)** [#34](https://github.com/nodejs/node-inspect/pull/34) - - [`802b88c`](https://github.com/nodejs/node-inspect/commit/802b88c8ad0a57608cb9e0cb4bf46ed683bb6344) **fix:** Delay run until breakpoints are restored - - [`2b93173`](https://github.com/nodejs/node-inspect/commit/2b93173d95e7f8b30d85603613cb2ae3b3ec18db) **fix:** Use single string for paused notice - - [`b4d5ee2`](https://github.com/nodejs/node-inspect/commit/b4d5ee2a3d25613b35a2e8e10a0eb75582cc5654) **fix:** Work around inconsistent handling of strict directive - - [`f6ccfc7`](https://github.com/nodejs/node-inspect/commit/f6ccfc7f4d00ad4fdf3b581b677f8d7f1699c44c) **fix:** Only restart after port is free - - [`8b101bf`](https://github.com/nodejs/node-inspect/commit/8b101bf669ca102df4980bfad3e0436ef1c2f1a4) **test:** Skip exact match on AIX -* [`a4e4b6f`](https://github.com/nodejs/node-inspect/commit/a4e4b6feeba4dedfd2c89ef32f39e813314d3bbd) **chore:** Fix repo info in package.json - - -### 1.10.5 - -* docs: minor edits to governance docs - **[@joshgav](https://github.com/joshgav)** [#17](https://github.com/buggerjs/node-inspect/pull/17) - - [`a70fe04`](https://github.com/buggerjs/node-inspect/commit/a70fe04bdde9b7c74588685066291f9b11183328) **docs:** minor edits to governance docs - - -### 1.10.4 - -* [`1c31bf7`](https://github.com/buggerjs/node-inspect/commit/1c31bf7d1b3ea1b424ae0662526596670cb506c9) **chore:** Support embedded mode - - -### 1.10.3 - -* [`7b20379`](https://github.com/buggerjs/node-inspect/commit/7b20379069af692a9038a31a4465f72db9eb532f) **chore:** Mark .eslintrc as root - - -### 1.10.2 - -* Run tests on windows - **[@jkrems](https://github.com/jkrems)** [#16](https://github.com/buggerjs/node-inspect/pull/16) - - [`5a57f98`](https://github.com/buggerjs/node-inspect/commit/5a57f9865e02eef0763c2a7f26236c34a632ccdd) **chore:** Run tests on windows - - [`0a04b50`](https://github.com/buggerjs/node-inspect/commit/0a04b50cc8b4dc6ce868927c635c479d75ce71f4) **chore:** Bump nlm to get rid of postinstall - - [`4a8b27c`](https://github.com/buggerjs/node-inspect/commit/4a8b27cea814a37895effd2a0c1b85dbfee3a7f4) **test:** Remove unix path assumptions - - -### 1.10.1 - -* [`4ba3c72`](https://github.com/buggerjs/node-inspect/commit/4ba3c72270fae9a71343ddca11aa27980678a67c) **refactor:** Undo weird bundling into one file - - -### 1.10.0 - -* [`3e1a66a`](https://github.com/buggerjs/node-inspect/commit/3e1a66a489bef19beaa5f859e99e027274ff43cb) **feat:** Support CPU & heap profiles - - -### 1.9.3 - -* Move back to single file - **[@jkrems](https://github.com/jkrems)** [#15](https://github.com/buggerjs/node-inspect/pull/15) - - [`9877660`](https://github.com/buggerjs/node-inspect/commit/9877660a73ff0ec0885ad7f939ba62020a46b4b6) **refactor:** Wrap client in IIFE - - [`7795c53`](https://github.com/buggerjs/node-inspect/commit/7795c533f0605eb128db610a5874b27e555251ef) **refactor:** Move more code in createRepl scope - - [`be34a39`](https://github.com/buggerjs/node-inspect/commit/be34a398e823612bdf5ac90bad5222af27035a00) **refactor:** Move back to single file - - [`ab45b62`](https://github.com/buggerjs/node-inspect/commit/ab45b6273dc0d3a49d3cf46a80cb48ab79d1caf8) **refactor:** Remove single-use functions - - [`37a711e`](https://github.com/buggerjs/node-inspect/commit/37a711ed5334c06ed4d85f995e567a9f176a68d5) **style:** Stop using `new Buffer` - - [`d669dc5`](https://github.com/buggerjs/node-inspect/commit/d669dc593f5ad5ca7a48f19f0905ef66ec0e540d) **chore:** Switch to node eslint rules - - [`15e7917`](https://github.com/buggerjs/node-inspect/commit/15e79177918d96dcffd2384715faf0308e97a26c) **style:** Use var in classical for loops - - -### 1.9.2 - -* [`c9dc4be`](https://github.com/buggerjs/node-inspect/commit/c9dc4beb08236e33d64f19417682cf5b3f5aeed6) **doc:** Link directly to GOVERNANCE file - - -### 1.9.1 - -* Handle big ws frames correctly - **[@jkrems](https://github.com/jkrems)** [#14](https://github.com/buggerjs/node-inspect/pull/14) - - [`f80100e`](https://github.com/buggerjs/node-inspect/commit/f80100e932710d232d074b239cbf8fefa564c789) **fix:** Handle big ws frames correctly - see: [#10](https://github.com/buggerjs/node-inspect/issues/10) - - -### 1.9.0 - -* Support for low-level agent access - **[@jkrems](https://github.com/jkrems)** [#13](https://github.com/buggerjs/node-inspect/pull/13) - - [`90ed431`](https://github.com/buggerjs/node-inspect/commit/90ed4310c62d130637c12f8ecdb752075c43ac36) **feat:** Support for low-level agent access - - -### 1.8.4 - -* Use proper path for websocket - **[@jkrems](https://github.com/jkrems)** [#12](https://github.com/buggerjs/node-inspect/pull/12) - - [`3405225`](https://github.com/buggerjs/node-inspect/commit/3405225979dfc2058bcc6d1b90f41c060dbd1f92) **fix:** Use proper path for websocket - see: [#11](https://github.com/buggerjs/node-inspect/issues/11) - - -### 1.8.3 - -* [`6f9883d`](https://github.com/buggerjs/node-inspect/commit/6f9883d4b29419831133988981b83e891b19739a) **fix:** Breakpoints & scripts work when not paused -* [`ecb1362`](https://github.com/buggerjs/node-inspect/commit/ecb1362c842e6ed5bc28c091a32bfd540742db75) **chore:** Pin node to 6.8 - - -### 1.8.2 - -* [`4219a98`](https://github.com/buggerjs/node-inspect/commit/4219a98d6514f1068feabce2945c21a0d5ba6561) **refactor:** Decouple source snippet from repl - - -### 1.8.1 - -* [`95402ee`](https://github.com/buggerjs/node-inspect/commit/95402ee5dff04057f074677d39db2f61ec74c151) **refactor:** Move `list` into CallFrame - - -### 1.8.0 - -* [`d0e6499`](https://github.com/buggerjs/node-inspect/commit/d0e6499084f5d656ef0c5fd470d3ab21f2e9a6b4) **feat:** `exec .scope` - - -### 1.7.0 - -* `breakOn{Exception,Uncaught,None}` - **[@jkrems](https://github.com/jkrems)** [#8](https://github.com/buggerjs/node-inspect/pull/8) - - [`fa8c4c7`](https://github.com/buggerjs/node-inspect/commit/fa8c4c7d7bb6972733c92da4d04fdd62c02b0e3b) **feat:** `breakOn{Exception,Uncaught,None}` - see: [#6](https://github.com/buggerjs/node-inspect/issues/6) - - -### 1.6.0 - -* Add `help` command - **[@jkrems](https://github.com/jkrems)** [#7](https://github.com/buggerjs/node-inspect/pull/7) - - [`09b37a0`](https://github.com/buggerjs/node-inspect/commit/09b37a02e04e16a38ce27f69538d3b098548b47c) **feat:** Add `help` command - see: [#5](https://github.com/buggerjs/node-inspect/issues/5) - - -### 1.5.0 - -* [`7e0fd99`](https://github.com/buggerjs/node-inspect/commit/7e0fd99fcfc65d8b647a2259df78f4cabf1d3d63) **feat:** Add `r` shortcut for `run` - - -### 1.4.1 - -* [`484d098`](https://github.com/buggerjs/node-inspect/commit/484d0983f06d6ff9639ab5197ba0a58313f532df) **chore:** Remove old implementation - - -### 1.4.0 - -* Properly tested implementation - **[@jkrems](https://github.com/jkrems)** [#4](https://github.com/buggerjs/node-inspect/pull/4) - - [`ba060d3`](https://github.com/buggerjs/node-inspect/commit/ba060d3ef65ae84df2a3a9b9f16d563f3c4b29be) **feat:** Error handling w/o args - - [`b39b3bc`](https://github.com/buggerjs/node-inspect/commit/b39b3bc07c13adc48fc8bb720889285c51e62548) **feat:** Launch child - - [`481693f`](https://github.com/buggerjs/node-inspect/commit/481693f676ee099b7787cd2426b980858e973602) **feat:** Connect debug client - - [`3bba0f2`](https://github.com/buggerjs/node-inspect/commit/3bba0f2416b2e3b4e6010de675003fcc328b16e8) **chore:** Disable lint for inactive code - - [`cc7bdfc`](https://github.com/buggerjs/node-inspect/commit/cc7bdfcf7f21ef5cd5c32c7800407238b0d4f100) **feat:** Properly fail with invalid host:port - - [`73f34f9`](https://github.com/buggerjs/node-inspect/commit/73f34f902634e9778597e129f46895aa8b643d72) **refactor:** Remove unused field - - [`6a23e0c`](https://github.com/buggerjs/node-inspect/commit/6a23e0cf3179f43ca6fc5a0fa2b1dd18ebc044b5) **refactor:** Better debug output & support node 6.6 - - [`63b0f9b`](https://github.com/buggerjs/node-inspect/commit/63b0f9b6ef8bd9af0f7cb14a5938a45838731fc9) **test:** Add timeout to waitFor(pattern) - - [`cfa197b`](https://github.com/buggerjs/node-inspect/commit/cfa197bf8325a1a4ca1b296f8d6971d368bfbfbb) **refactor:** Move REPL setup into own file - - [`3f46c2c`](https://github.com/buggerjs/node-inspect/commit/3f46c2c43f836e1135b66871087aa74969f6b330) **feat:** Working repl eval - - [`6911eb1`](https://github.com/buggerjs/node-inspect/commit/6911eb1a00b964bc5683506d433fa4f665f5a82c) **feat:** Enter repeats last command - - [`7d20b7d`](https://github.com/buggerjs/node-inspect/commit/7d20b7deadf1b251ea8cf2cc9167c175624932c4) **chore:** Add missing license header - - [`23c62f8`](https://github.com/buggerjs/node-inspect/commit/23c62f8375ca7c8b71d032047e728dace02f4efa) **feat:** Print break context - - [`5dbc83d`](https://github.com/buggerjs/node-inspect/commit/5dbc83df31171f9c38a974c99340bde26f2e24ec) **feat:** Stepping and breakpoints - - [`8deb8cc`](https://github.com/buggerjs/node-inspect/commit/8deb8cc36b9fca432ab8df63a82e9de7ab5adaf0) **feat:** list for printing source - - [`1ed2ec9`](https://github.com/buggerjs/node-inspect/commit/1ed2ec9937070652be611dbb6b11dfb42cb840f8) **chore:** Disable verbose output on CI - - [`625a435`](https://github.com/buggerjs/node-inspect/commit/625a435925dd8fd980bed2dc9e3fd73dd27df4ef) **fix:** Gracefully handle delayed scriptParsed - - [`8823c60`](https://github.com/buggerjs/node-inspect/commit/8823c60d347600b2313cfdd8cb5e96fe02419a8a) **chore:** Run all the tests - - [`00506f7`](https://github.com/buggerjs/node-inspect/commit/00506f763928cc440505a81030167a11b9a84e00) **feat:** backtrace/bt - - [`e1ee02d`](https://github.com/buggerjs/node-inspect/commit/e1ee02d5cc389916489d387d07d5dd161230427a) **refactor:** Leverage util.inspect.custom - - [`5dcc319`](https://github.com/buggerjs/node-inspect/commit/5dcc31922d40f56c7435319d1538390a442e8e4b) **feat:** scripts and scripts(true) - - [`085cd5a`](https://github.com/buggerjs/node-inspect/commit/085cd5a76a961edfcaa342fff5eb09bf2f9c8983) **refactor:** Consistent import style - - [`1c60f91`](https://github.com/buggerjs/node-inspect/commit/1c60f91f233848c05d865617dc7f5aacb36270b6) **feat:** Set breakpoint before file is loaded - - [`bc82ecc`](https://github.com/buggerjs/node-inspect/commit/bc82eccb2a1a7c0f5332371254f6584e748216aa) **feat:** breakpoints to list breakpoints - - [`7f48c95`](https://github.com/buggerjs/node-inspect/commit/7f48c9510696ec400d51afaca8d23a9c292640f8) **feat:** watchers & exec - - [`0f8cd13`](https://github.com/buggerjs/node-inspect/commit/0f8cd13a092e5dbeb395ff04cbe2ed97cb986423) **feat:** clearBreakpoint - - [`0d31560`](https://github.com/buggerjs/node-inspect/commit/0d315603bdcb9f4da42fab24dc569c325151269e) **feat:** version to print v8 version - - [`df6b89d`](https://github.com/buggerjs/node-inspect/commit/df6b89df580a9afcb3b8883b0e4224cbcebb384f) **feat:** Paused & global exec - - [`9e97d73`](https://github.com/buggerjs/node-inspect/commit/9e97d73073ceffd70974d45887c84fadb9159d5c) **feat:** repl to enter exec mode - - [`9ee9f90`](https://github.com/buggerjs/node-inspect/commit/9ee9f903d6202f54ed2b3b3559da4006b65d39b5) **feat:** run & restart -* [`3a752aa`](https://github.com/buggerjs/node-inspect/commit/3a752aaa773968bfe16c5f543bd739feed598bea) **feat:** kill -* [`a67e470`](https://github.com/buggerjs/node-inspect/commit/a67e47018b20d46aeeaa7abd27eb8e7770fd0b8f) **feat:** Restore breakpoints on restart - - -### 1.3.3 - -* [`eb7a54c`](https://github.com/buggerjs/node-inspect/commit/eb7a54c6fa731ed3276072c72034046fc5ffbac6) **chore:** Switch to tap for tests - - -### 1.3.2 - -* Add notes about governance - **[@jkrems](https://github.com/jkrems)** [#3](https://github.com/buggerjs/node-inspect/pull/3) - - [`e94089d`](https://github.com/buggerjs/node-inspect/commit/e94089d93689cacf5c953e94563463d1e174452d) **chore:** Add notes about governance - - -### 1.3.1 - -* [`8767137`](https://github.com/buggerjs/node-inspect/commit/8767137c53a2f6b1d36970074ea95be9871e50e3) **style:** Remove rogue console.log - - -### 1.3.0 - -* [`3ac6232`](https://github.com/buggerjs/node-inspect/commit/3ac623219ba44b0af40ef66826610a26a46c7966) **feat:** Add `version` command - - -### 1.2.0 - -* [`86b5812`](https://github.com/buggerjs/node-inspect/commit/86b581218ccab44e6bde259a17ad1e71645a6137) **feat:** scripts & listScripts(true) - - -### 1.1.1 - -* [`feaea38`](https://github.com/buggerjs/node-inspect/commit/feaea385a981e6b72a8d99277fbf575c54e15fc6) **style:** Typo in comment - - -### 1.1.0 - -* [`c64155f`](https://github.com/buggerjs/node-inspect/commit/c64155faa552f71463842a26330aa5bcbfc31670) **feat:** repl command - - -### 1.0.0 - -* [`44c4c79`](https://github.com/buggerjs/node-inspect/commit/44c4c79af5a228ccfd8906f11409b2a33390b878) **chore:** Initial commit -* [`985873c`](https://github.com/buggerjs/node-inspect/commit/985873cfb97146b38480080f9907219c473f1f6f) **feat:** Launching the example works -* [`3d92d05`](https://github.com/buggerjs/node-inspect/commit/3d92d05cca152a2c2647aa64eefc80432638bc4d) **chore:** Proper license and passing tests -* [`b3f99d9`](https://github.com/buggerjs/node-inspect/commit/b3f99d981038b17663fcfd984d2f5d6d9b51ee18) **feat:** Futile attempts to send a valid ws frame -* [`465cfb7`](https://github.com/buggerjs/node-inspect/commit/465cfb7b295aebb48b285c26f6de9c4657fe590d) **feat:** Working ws connection -* [`da9f011`](https://github.com/buggerjs/node-inspect/commit/da9f01118e2b144f2da8cd370113a608526774a1) **fix:** Fix remote connect -* [`5ef33d7`](https://github.com/buggerjs/node-inspect/commit/5ef33d7892cc49becb4c66098fc7927bc74b014a) **feat:** Working step-by-step -* [`534e1e4`](https://github.com/buggerjs/node-inspect/commit/534e1e46b307d61d51eb4c0aab4a3b17c17aea3d) **chore:** Add bin entry -* [`8cff9cf`](https://github.com/buggerjs/node-inspect/commit/8cff9cfb0138b5ecff0f5f6a7839dbfddc0684fd) **style:** Use simpler key thingy -* [`720ec53`](https://github.com/buggerjs/node-inspect/commit/720ec53a5b251ab3caf27f06b60924efb9e03a92) **doc:** Add instructions -* [`b89ad60`](https://github.com/buggerjs/node-inspect/commit/b89ad601b885a417e6433b1609477d8453f498a1) **doc:** More helpful docs -* [`de9243c`](https://github.com/buggerjs/node-inspect/commit/de9243c95eabe733d05952229340808c3cebf129) **feat:** Watchers -* [`e16978f`](https://github.com/buggerjs/node-inspect/commit/e16978ff8e4b2b2bdccf88fd7d3905f525822981) **docs:** Working usage hints -* [`2dbc204`](https://github.com/buggerjs/node-inspect/commit/2dbc2042145fd97169fc7536186a449715e27810) **refactor:** Use proxies -* [`b8c9b14`](https://github.com/buggerjs/node-inspect/commit/b8c9b147713f63181396d5a7fe4c2f737b733b4c) **style:** Remove unused var -* [`f6b4b20`](https://github.com/buggerjs/node-inspect/commit/f6b4b20a1d28d91cfe452b995f7dbe5f7c749e89) **feat:** Nicer inspect of remote values -* [`36887c6`](https://github.com/buggerjs/node-inspect/commit/36887c66bbf26d540f087f80ddfec38462a33bdf) **fix:** Properly print watchers -* [`7729442`](https://github.com/buggerjs/node-inspect/commit/77294426157a28cc76e339cb13916a205182641e) **feat:** Add pause command -* [`e39a713`](https://github.com/buggerjs/node-inspect/commit/e39a7134873f06da37baaa9b6252cede4ad38d7a) **fix:** Properly format boolean properties -* [`f8f51d7`](https://github.com/buggerjs/node-inspect/commit/f8f51d7a01e8d74023306a08a3d6e2da63d123e1) **fix:** Properly format numeric properties -* [`89e6e08`](https://github.com/buggerjs/node-inspect/commit/89e6e087220f3c3cb628ac7541c44298485a2e04) **feat:** Add backtrace command -* [`82362ac`](https://github.com/buggerjs/node-inspect/commit/82362acfc7ce22b4cccc64889ec136dedc8895ec) **feat:** Add setBreakpoint() -* [`7064cce`](https://github.com/buggerjs/node-inspect/commit/7064ccec3b103683088d532abfe5b4e7c066948b) **feat:** Add `setBreakpoint(line)` -* [`360580e`](https://github.com/buggerjs/node-inspect/commit/360580eba4353e81311e56df018eec0ca233da11) **feat:** Add run/kill/restart -* [`b1b576e`](https://github.com/buggerjs/node-inspect/commit/b1b576e2645723a8575df544e0bfb672d60d9d91) **feat:** Add `help` command -* [`2db4660`](https://github.com/buggerjs/node-inspect/commit/2db46609cd1c8543d31ebd5dc47e4c27ec254841) **feat:** Add remaining sb() variants -* [`f2ad1ae`](https://github.com/buggerjs/node-inspect/commit/f2ad1aeedafb154043d70bb9195b10986d311d26) **fix:** Display breakpoints set into the future -* [`73272f9`](https://github.com/buggerjs/node-inspect/commit/73272f9ace1f8546f8cad1d53627dbffba50bb4e) **refactor:** Make breakpoints more inspect friendly -* [`507a71d`](https://github.com/buggerjs/node-inspect/commit/507a71de345a3de7fe144517e9f5ea264ff993e3) **feat:** Add breakpoints command -* [`5fb3e5d`](https://github.com/buggerjs/node-inspect/commit/5fb3e5d17bbcfd45b264431547b3cf0b781c7640) **docs:** Link to Command Line API docs -* [`81af501`](https://github.com/buggerjs/node-inspect/commit/81af501bbf85397e2078310c7f24a9ac5b7f02dc) **chore:** Fix license field diff --git a/deps/node-inspect/CONTRIBUTING.md b/deps/node-inspect/CONTRIBUTING.md deleted file mode 100644 index 012d29471462de..00000000000000 --- a/deps/node-inspect/CONTRIBUTING.md +++ /dev/null @@ -1,181 +0,0 @@ -# Contributing - -🎉🏅 Thanks for helping us improve this project! 🙏 - -This document outlines some of the practices we care about. -If you have any questions or suggestions about the process, -feel free to [open an issue](#reporting-issues). - -## Code of Conduct - -The [Node.js Code of Conduct][] applies to this repo. - -[Node.js Code of Conduct]: https://github.com/nodejs/node/blob/master/CODE_OF_CONDUCT.md - -## Governance - -This project falls under the governance of the Node.js Diagnostics WG as -described at . - -## Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -* (a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -* (b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -* (c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -* (d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. - -## How Can I Contribute? - -### Reporting Issues - -If you find any mistakes in the docs or a bug in the code, -please [open an issue in Github](https://github.com/nodejs/node-inspect/issues/new) so we can look into it. -You can also [create a PR](#contributing-code) fixing it yourself of course. - -If you report a bug, please follow these guidelines: - -* Make sure the bug exists in the latest version. -* Include instructions on how to reproduce the issue. - The instructions should be as minimal as possible - and answer the three big questions: - 1. What are the exact steps you took? This includes the exact versions of node, npm, and any packages involved. - 1. What result are you expecting? - 1. What is the actual result? - -### Improving Documentation - -For small documentation changes, you can use [Github's editing feature](https://help.github.com/articles/editing-files-in-another-user-s-repository/). -The only thing to keep in mind is to prefix the commit message with "docs: ". -The default commit message generated by Github will lead to a failing CI build. - -For larger updates to the documentation -it might be better to follow the [instructions for contributing code below](#contributing-code). - -### Contributing Code - -**Note:** If you're planning on making substantial changes, -please [open an issue first to discuss your idea](#reporting-issues). -Otherwise you might end up investing a lot of work -only to discover that it conflicts with plans the maintainers might have. - -The general steps for creating a pull request are: - -1. Create a branch for your change. - Always start your branch from the latest `master`. - We often prefix the branch name with our initials, e.g. `jk-a-change`. -1. Run `npm install` to install the dependencies. -1. If you're fixing a bug, be sure to write a test *first*. - That way you can validate that the test actually catches the bug and doesn't pass. -1. Make your changes to the code. - Remember to update the tests if you add new features or change behavior. -1. Run the tests via `npm test`. This will also run style checks and other validations. - You might see errors about uncommitted files. - This is expected until you commit your changes. -1. Once you're done, `git add .` and `git commit`. - Please follow the [commit message conventions](#commits--commit-messages) described below. -1. Push your branch to Github & create a PR. - -#### Code Style - -In addition to any linting rules the project might include, -a few general rules of thumb: - -* Try to match the style of the rest of the code. -* We prefer simple code that is easy to understand over terse, expressive code. -* We try to structure projects by semantics instead of role. - E.g. we'd rather have a `tree.js` module that contains tree traversal-related helpers - than a `helpers.js` module. -* Actually, if you create helpers you might want to put those into a separate package. - That way it's easier to reuse them. - -#### Commits & Commit Messages - -Please follow the [angular commit message conventions](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines). -We use an automated tool for generating releases -that depends on the conventions to determine the next version and the content of the changelog. -Commit messages that don't follow the conventions will cause `npm test` (and thus CI) to fail. - -The short summary - a commit message should look like this: - -``` -: - - - - - -