Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit 6052946

Browse files
authored
Merge pull request #34 from nodejs/jk-delay-run
Delay run until breakpoints are restored
2 parents d54a448 + 8b101bf commit 6052946

15 files changed

+143
-49
lines changed

examples/alive.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
'use strict';
21
let x = 0;
32
function heartbeat() {
43
++x;

examples/backtrace.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
'use strict';
21
const { exports: moduleScoped } = module;
32

43
function topFn(a, b = false) {

examples/cjs/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
'use strict';
1+
const fourty = 40;
22
const { add } = require('./other');
33

4-
const sum = add(40, 2);
4+
const sum = add(fourty, 2);
55
module.exports = sum;

examples/cjs/other.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
'use strict';
21
exports.add = function add(a, b) {
32
return a + b;
43
};

examples/exceptions.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
'use strict';
21
let error = null;
32
try {
43
throw new Error('Caught');

examples/three-lines.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
'use strict';
21
let x = 1;
32
x = x + 1;
43
module.exports = x;

examples/use-strict.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
'use strict';
2+
console.log('first real line');

lib/_inspect.js

+58-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323
const { spawn } = require('child_process');
2424
const { EventEmitter } = require('events');
25+
const net = require('net');
2526
const util = require('util');
2627

2728
const runAsStandalone = typeof __dirname !== 'undefined';
@@ -99,6 +100,45 @@ function createAgentProxy(domain, client) {
99100
});
100101
}
101102

103+
function portIsFree(host, port, timeout = 2000) {
104+
const retryDelay = 150;
105+
let didTimeOut = false;
106+
107+
return new Promise((resolve, reject) => {
108+
setTimeout(() => {
109+
didTimeOut = true;
110+
reject(new Error(
111+
`Timeout (${timeout}) waiting for ${host}:${port} to be free`));
112+
}, timeout);
113+
114+
function pingPort() {
115+
if (didTimeOut) return;
116+
117+
const socket = net.connect(port, host);
118+
let didRetry = false;
119+
function retry() {
120+
if (!didRetry && !didTimeOut) {
121+
didRetry = true;
122+
setTimeout(pingPort, retryDelay);
123+
}
124+
}
125+
126+
socket.on('error', (error) => {
127+
if (error.code === 'ECONNREFUSED') {
128+
resolve();
129+
} else {
130+
retry();
131+
}
132+
});
133+
socket.on('connect', () => {
134+
socket.destroy();
135+
retry();
136+
});
137+
}
138+
pingPort();
139+
});
140+
}
141+
102142
class NodeInspector {
103143
constructor(options, stdin, stdout) {
104144
this.options = options;
@@ -139,8 +179,9 @@ class NodeInspector {
139179
process.once('SIGHUP', process.exit.bind(process, 0));
140180

141181
this.run()
142-
.then(() => {
143-
this.repl = startRepl();
182+
.then(() => startRepl())
183+
.then((repl) => {
184+
this.repl = repl;
144185
this.repl.on('exit', () => {
145186
process.exit(0);
146187
});
@@ -150,15 +191,19 @@ class NodeInspector {
150191
}
151192

152193
suspendReplWhile(fn) {
153-
this.repl.rli.pause();
194+
if (this.repl) {
195+
this.repl.rli.pause();
196+
}
154197
this.stdin.pause();
155198
this.paused = true;
156199
return new Promise((resolve) => {
157200
resolve(fn());
158201
}).then(() => {
159202
this.paused = false;
160-
this.repl.rli.resume();
161-
this.repl.displayPrompt();
203+
if (this.repl) {
204+
this.repl.rli.resume();
205+
this.repl.displayPrompt();
206+
}
162207
this.stdin.resume();
163208
}).then(null, (error) => process.nextTick(() => { throw error; }));
164209
}
@@ -173,7 +218,14 @@ class NodeInspector {
173218

174219
run() {
175220
this.killChild();
176-
return this._runScript().then((child) => {
221+
const { host, port } = this.options;
222+
223+
const runOncePortIsFree = () => {
224+
return portIsFree(host, port)
225+
.then(() => this._runScript());
226+
};
227+
228+
return runOncePortIsFree().then((child) => {
177229
this.child = child;
178230

179231
let connectionAttempts = 0;
@@ -198,7 +250,6 @@ class NodeInspector {
198250
});
199251
};
200252

201-
const { host, port } = this.options;
202253
this.print(`connecting to ${host}:${port} ..`, true);
203254
return attemptConnect();
204255
});

lib/internal/inspect_client.js

+1-14
Original file line numberDiff line numberDiff line change
@@ -334,20 +334,7 @@ class Client extends EventEmitter {
334334
this.emit('close');
335335
});
336336

337-
Promise.all([
338-
this.callMethod('Runtime.enable'),
339-
this.callMethod('Debugger.enable'),
340-
this.callMethod('Debugger.setPauseOnExceptions', { state: 'none' }),
341-
this.callMethod('Debugger.setAsyncCallStackDepth', { maxDepth: 0 }),
342-
this.callMethod('Profiler.enable'),
343-
this.callMethod('Profiler.setSamplingInterval', { interval: 100 }),
344-
this.callMethod('Debugger.setBlackboxPatterns', { patterns: [] }),
345-
this.callMethod('Runtime.runIfWaitingForDebugger'),
346-
]).then(() => {
347-
this.emit('ready');
348-
}, (error) => {
349-
this.emit('error', error);
350-
});
337+
this.emit('ready');
351338
};
352339

353340
return new Promise((resolve, reject) => {

lib/internal/inspect_repl.js

+34-13
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,8 @@ function createRepl(inspector) {
748748
.filter(({ location }) => !!location.scriptUrl)
749749
.map(({ location }) =>
750750
setBreakpoint(location.scriptUrl, location.lineNumber + 1));
751-
if (!newBreakpoints.length) return;
752-
Promise.all(newBreakpoints).then((results) => {
751+
if (!newBreakpoints.length) return Promise.resolve();
752+
return Promise.all(newBreakpoints).then((results) => {
753753
print(`${results.length} breakpoints restored.`);
754754
});
755755
}
@@ -770,16 +770,19 @@ function createRepl(inspector) {
770770
const breakType = reason === 'other' ? 'break' : reason;
771771
const script = knownScripts[scriptId];
772772
const scriptUrl = script ? getRelativePath(script.url) : '[unknown]';
773-
print(`${breakType} in ${scriptUrl}:${lineNumber + 1}`);
773+
774+
const header = `${breakType} in ${scriptUrl}:${lineNumber + 1}`;
774775

775776
inspector.suspendReplWhile(() =>
776777
Promise.all([formatWatchers(true), selectedFrame.list(2)])
777778
.then(([watcherList, context]) => {
778779
if (watcherList) {
779780
return `${watcherList}\n${inspect(context)}`;
780781
}
781-
return context;
782-
}).then(print));
782+
return inspect(context);
783+
}).then((breakContext) => {
784+
print(`${header}\n${breakContext}`);
785+
}));
783786
});
784787

785788
function handleResumed() {
@@ -1026,7 +1029,30 @@ function createRepl(inspector) {
10261029
aliasProperties(context, SHORTCUTS);
10271030
}
10281031

1032+
function initAfterStart() {
1033+
const setupTasks = [
1034+
Runtime.enable(),
1035+
Profiler.enable(),
1036+
Profiler.setSamplingInterval({ interval: 100 }),
1037+
Debugger.enable(),
1038+
Debugger.setPauseOnExceptions({ state: 'none' }),
1039+
Debugger.setAsyncCallStackDepth({ maxDepth: 0 }),
1040+
Debugger.setBlackboxPatterns({ patterns: [] }),
1041+
Debugger.setPauseOnExceptions({ state: pauseOnExceptionState }),
1042+
restoreBreakpoints(),
1043+
Runtime.runIfWaitingForDebugger(),
1044+
];
1045+
return Promise.all(setupTasks);
1046+
}
1047+
10291048
return function startRepl() {
1049+
inspector.client.on('close', () => {
1050+
resetOnStart();
1051+
});
1052+
inspector.client.on('ready', () => {
1053+
initAfterStart();
1054+
});
1055+
10301056
const replOptions = {
10311057
prompt: 'debug> ',
10321058
input: inspector.stdin,
@@ -1035,6 +1061,7 @@ function createRepl(inspector) {
10351061
useGlobal: false,
10361062
ignoreUndefined: true,
10371063
};
1064+
10381065
repl = Repl.start(replOptions); // eslint-disable-line prefer-const
10391066
initializeContext(repl.context);
10401067
repl.on('reset', initializeContext);
@@ -1044,14 +1071,8 @@ function createRepl(inspector) {
10441071
repl.rli.emit('SIGINT');
10451072
});
10461073

1047-
inspector.client.on('close', () => {
1048-
resetOnStart();
1049-
});
1050-
1051-
inspector.client.on('ready', () => {
1052-
restoreBreakpoints();
1053-
Debugger.setPauseOnExceptions({ state: pauseOnExceptionState });
1054-
});
1074+
// Init once for the initial connection
1075+
initAfterStart();
10551076

10561077
return repl;
10571078
};

test/cli/backtrace.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ test('display and navigate backtrace', (t) => {
1919
.then(() => cli.stepCommand('c'))
2020
.then(() => cli.command('bt'))
2121
.then(() => {
22-
t.match(cli.output, `#0 topFn ${script}:8:2`);
22+
t.match(cli.output, `#0 topFn ${script}:7:2`);
2323
})
2424
.then(() => cli.command('backtrace'))
2525
.then(() => {
26-
t.match(cli.output, `#0 topFn ${script}:8:2`);
26+
t.match(cli.output, `#0 topFn ${script}:7:2`);
2727
})
2828
.then(() => cli.quit())
2929
.then(null, onFatal);

test/cli/exceptions.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ test('break on (uncaught) exceptions', (t) => {
3131
.then(() => cli.command('breakOnException'))
3232
.then(() => cli.stepCommand('c'))
3333
.then(() => {
34-
t.match(cli.output, `exception in ${script}:4`);
34+
t.match(cli.output, `exception in ${script}:3`);
3535
})
3636
.then(() => cli.stepCommand('c'))
3737
.then(() => {
38-
t.match(cli.output, `exception in ${script}:10`);
38+
t.match(cli.output, `exception in ${script}:9`);
3939
})
4040

4141
// Next run: With `breakOnUncaught` it only pauses on the 2nd exception
@@ -46,7 +46,7 @@ test('break on (uncaught) exceptions', (t) => {
4646
})
4747
.then(() => cli.stepCommand('c'))
4848
.then(() => {
49-
t.match(cli.output, `exception in ${script}:10`);
49+
t.match(cli.output, `exception in ${script}:9`);
5050
})
5151

5252
// Next run: Back to the initial state! It should die again.

test/cli/launch.test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ const startCLI = require('./start-cli');
88
test('examples/empty.js', (t) => {
99
const script = Path.join('examples', 'empty.js');
1010
const cli = startCLI([script]);
11-
return cli.waitForPrompt()
11+
12+
return cli.waitFor(/break/)
13+
.then(() => cli.waitForPrompt())
1214
.then(() => {
1315
t.match(cli.output, 'debug>', 'prints a prompt');
1416
t.match(

test/cli/preserve-breaks.test.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,17 @@ test('run after quit / restart', (t) => {
4848
})
4949
.then(() => cli.command('breakpoints'))
5050
.then(() => {
51-
t.match(cli.output, `#0 ${script}:2`);
52-
t.match(cli.output, `#1 ${script}:3`);
51+
if (process.platform === 'aix') {
52+
// TODO: There is a known issue on AIX where the breakpoints aren't
53+
// properly resolved yet when we reach this point.
54+
// Eventually that should be figured out but for now we don't want
55+
// to fail builds because of it.
56+
t.match(cli.output, /#0 [^\n]+three-lines\.js\$?:2/);
57+
t.match(cli.output, /#1 [^\n]+three-lines\.js\$?:3/);
58+
} else {
59+
t.match(cli.output, `#0 ${script}:2`);
60+
t.match(cli.output, `#1 ${script}:3`);
61+
}
5362
})
5463
.then(() => cli.quit())
5564
.then(null, onFatal);

test/cli/use-strict.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
const Path = require('path');
3+
4+
const { test } = require('tap');
5+
6+
const startCLI = require('./start-cli');
7+
8+
test('for whiles that starts with strict directive', (t) => {
9+
const script = Path.join('examples', 'use-strict.js');
10+
const cli = startCLI([script]);
11+
12+
function onFatal(error) {
13+
cli.quit();
14+
throw error;
15+
}
16+
17+
return cli.waitFor(/break/)
18+
.then(() => cli.waitForPrompt())
19+
.then(() => {
20+
t.match(
21+
cli.output,
22+
/break in [^:]+:(?:1|2)[^\d]/,
23+
'pauses either on strict directive or first "real" line');
24+
})
25+
.then(() => cli.quit())
26+
.then(null, onFatal);
27+
});

0 commit comments

Comments
 (0)