Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit a32a243

Browse files
bajtosbnoordhuis
authored andcommitted
debugger: breakpoints in scripts not loaded yet
When developer calls setBreakpoint with an unknown script name, we convert the script name into regular expression matching all paths ending with given name (name can be a relative path too). To create such breakpoint in V8, we use type `scriptRegEx` instead of `scriptId` for `setbreakpoint` request. To restore such breakpoint, we save the original script name send by the user. We use this original name to set (restore) breakpoint in the new child process. This is a back-port of commit 5db936d from the master branch.
1 parent 2cf7e5d commit a32a243

File tree

6 files changed

+175
-23
lines changed

6 files changed

+175
-23
lines changed

doc/api/debugger.markdown

+24
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,30 @@ functions body
109109
script.js
110110
* `clearBreakpoint`, `cb(...)` - Clear breakpoint
111111

112+
It is also possible to set a breakpoint in a file (module) that
113+
isn't loaded yet:
114+
115+
% ./node debug test/fixtures/break-in-module/main.js
116+
< debugger listening on port 5858
117+
connecting to port 5858... ok
118+
break in test/fixtures/break-in-module/main.js:1
119+
1 var mod = require('./mod.js');
120+
2 mod.hello();
121+
3 mod.hello();
122+
debug> setBreakpoint('mod.js', 23)
123+
Warning: script 'mod.js' was not loaded yet.
124+
1 var mod = require('./mod.js');
125+
2 mod.hello();
126+
3 mod.hello();
127+
debug> c
128+
break in test/fixtures/break-in-module/mod.js:23
129+
21
130+
22 exports.hello = function() {
131+
23 return 'hello from module';
132+
24 };
133+
25
134+
debug>
135+
112136
### Info
113137

114138
* `backtrace`, `bt` - Print backtrace of current execution frame

lib/_debugger.js

+34-21
Original file line numberDiff line numberDiff line change
@@ -1385,16 +1385,28 @@ Interface.prototype.setBreakpoint = function(script, line,
13851385
scriptId = script;
13861386
}
13871387

1388-
if (!scriptId) return this.error('Script : ' + script + ' not found');
13891388
if (ambiguous) return this.error('Script name is ambiguous');
13901389
if (line <= 0) return this.error('Line should be a positive value');
13911390

1392-
var req = {
1393-
type: 'scriptId',
1394-
target: scriptId,
1395-
line: line - 1,
1396-
condition: condition
1397-
};
1391+
var req;
1392+
if (scriptId) {
1393+
req = {
1394+
type: 'scriptId',
1395+
target: scriptId,
1396+
line: line - 1,
1397+
condition: condition
1398+
};
1399+
} else {
1400+
this.print('Warning: script \'' + script + '\' was not loaded yet.');
1401+
var escapedPath = script.replace(/([/\\.?*()^${}|[\]])/g, '\\$1');
1402+
var scriptPathRegex = '^(.*[\\/\\\\])?' + escapedPath + '$';
1403+
req = {
1404+
type: 'scriptRegExp',
1405+
target: scriptPathRegex,
1406+
line: line - 1,
1407+
condition: condition
1408+
};
1409+
}
13981410
}
13991411

14001412
self.pause();
@@ -1411,20 +1423,18 @@ Interface.prototype.setBreakpoint = function(script, line,
14111423
// Try load scriptId and line from response
14121424
if (!scriptId) {
14131425
scriptId = res.script_id;
1414-
line = res.line;
1415-
}
1416-
1417-
// If we finally have one - remember this breakpoint
1418-
if (scriptId) {
1419-
self.client.breakpoints.push({
1420-
id: res.breakpoint,
1421-
scriptId: scriptId,
1422-
script: (self.client.scripts[scriptId] || {}).name,
1423-
line: line,
1424-
condition: condition
1425-
});
1426+
line = res.line + 1;
14261427
}
14271428

1429+
// Remember this breakpoint even if scriptId is not resolved yet
1430+
self.client.breakpoints.push({
1431+
id: res.breakpoint,
1432+
scriptId: scriptId,
1433+
script: (self.client.scripts[scriptId] || {}).name,
1434+
line: line,
1435+
condition: condition,
1436+
scriptReq: script
1437+
});
14281438
}
14291439
self.resume();
14301440
});
@@ -1439,7 +1449,9 @@ Interface.prototype.clearBreakpoint = function(script, line) {
14391449
index;
14401450

14411451
this.client.breakpoints.some(function(bp, i) {
1442-
if (bp.scriptId === script || bp.script.indexOf(script) !== -1) {
1452+
if (bp.scriptId === script ||
1453+
bp.scriptReq === script ||
1454+
(bp.script && bp.script.indexOf(script) !== -1)) {
14431455
if (index !== undefined) {
14441456
ambiguous = true;
14451457
}
@@ -1657,7 +1669,8 @@ Interface.prototype.trySpawn = function(cb) {
16571669

16581670
// Restore breakpoints
16591671
breakpoints.forEach(function(bp) {
1660-
self.setBreakpoint(bp.scriptId, bp.line, bp.condition, true);
1672+
self.print('Restoring breakpoint ' + bp.scriptReq + ':' + bp.line);
1673+
self.setBreakpoint(bp.scriptReq, bp.line, bp.condition, true);
16611674
});
16621675

16631676
client.on('close', function() {

test/fixtures/break-in-module/main.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var mod = require('./mod.js');
2+
mod.hello();
3+
mod.hello();
4+
debugger;

test/fixtures/break-in-module/mod.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
exports.hello = function() {
23+
return 'hello from module';
24+
};

test/simple/helper-debugger-repl.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,23 @@ function addTest(input, output) {
126126
expected.push({input: input, lines: output, callback: next});
127127
}
128128

129-
var initialLines = [
129+
var handshakeLines = [
130130
/listening on port \d+/,
131-
/connecting.* ok/,
131+
/connecting.* ok/
132+
];
133+
134+
var initialBreakLines = [
132135
/break in .*:1/,
133136
/1/, /2/, /3/
134137
];
135138

139+
var initialLines = handshakeLines.concat(initialBreakLines);
140+
136141
// Process initial lines
137142
addTest(null, initialLines);
138143

139144
exports.startDebugger = startDebugger;
140145
exports.addTest = addTest;
141146
exports.initialLines = initialLines;
147+
exports.handshakeLines = handshakeLines;
148+
exports.initialBreakLines = initialBreakLines;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var repl = require('./helper-debugger-repl.js');
23+
24+
repl.startDebugger('break-in-module/main.js');
25+
26+
// -- SET BREAKPOINT --
27+
28+
// Set breakpoint by file name + line number where the file is not loaded yet
29+
repl.addTest('sb("mod.js", 23)', [
30+
/Warning: script 'mod\.js' was not loaded yet\./,
31+
/1/, /2/, /3/, /4/, /5/, /6/
32+
]);
33+
34+
// Check escaping of regex characters
35+
repl.addTest('sb(")^$*+?}{|][(.js\\\\", 1)', [
36+
/Warning: script '[^']+' was not loaded yet\./,
37+
/1/, /2/, /3/, /4/, /5/, /6/
38+
]);
39+
40+
// continue - the breakpoint should be triggered
41+
repl.addTest('c', [
42+
/break in .*[\\\/]mod\.js:23/,
43+
/21/, /22/, /23/, /24/, /25/
44+
]);
45+
46+
// -- RESTORE BREAKPOINT ON RESTART --
47+
48+
// Restart the application - breakpoint should be restored
49+
repl.addTest('restart', [].concat(
50+
[
51+
/terminated/
52+
],
53+
repl.handshakeLines,
54+
[
55+
/Restoring breakpoint mod.js:23/,
56+
/Warning: script 'mod\.js' was not loaded yet\./,
57+
/Restoring breakpoint \).*:\d+/,
58+
/Warning: script '\)[^']*' was not loaded yet\./
59+
],
60+
repl.initialBreakLines));
61+
62+
// continue - the breakpoint should be triggered
63+
repl.addTest('c', [
64+
/break in .*[\\\/]mod\.js:23/,
65+
/21/, /22/, /23/, /24/, /25/
66+
]);
67+
68+
// -- CLEAR BREAKPOINT SET IN MODULE TO BE LOADED --
69+
70+
repl.addTest('cb("mod.js", 23)', [
71+
/18/, /./, /./, /./, /./, /./, /./, /./, /26/
72+
]);
73+
74+
repl.addTest('c', [
75+
/break in .*[\\\/]main\.js:4/,
76+
/2/, /3/, /4/, /5/, /6/
77+
]);
78+
79+
// -- (END) --
80+
repl.addTest('quit', []);

0 commit comments

Comments
 (0)