Restore breakpoints on debugee restart.
When the debugged application is restarted, node-inspector restores all breakpoints from the debugging session.
This commit is contained in:
Родитель
b541de3cca
Коммит
87c71616ff
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Creates an immutable representation of a breakpoint.
|
||||
*
|
||||
* @param {{
|
||||
* sourceID: number,
|
||||
* url: string,
|
||||
* line: number,
|
||||
* enabled: boolean,
|
||||
* condition: string,
|
||||
* number: number
|
||||
* }} props
|
||||
* @returns {Breakpoint}
|
||||
* @constructor
|
||||
*/
|
||||
function Breakpoint(props) {
|
||||
var sourceID = props.sourceID !== undefined && props.sourceID !== null
|
||||
? props.sourceID.toString()
|
||||
: '';
|
||||
|
||||
return Object.create(Breakpoint.prototype, {
|
||||
sourceID: { value: sourceID, enumerable: true },
|
||||
url: { value: props.url, enumerable: true },
|
||||
line: { value: props.line, enumerable: true },
|
||||
enabled: {value: props.enabled, enumerable: true},
|
||||
condition: {value: props.condition, enumerable: true },
|
||||
number: {value: props.number, enumerable: true },
|
||||
key: {
|
||||
get: function() {
|
||||
return this.sourceID + ':' + this.line;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Breakpoint.prototype = {
|
||||
createRequest: function() {
|
||||
return {
|
||||
arguments: {
|
||||
type: 'script',
|
||||
target: this.url,
|
||||
line: this.line - 1,
|
||||
enabled: this.enabled,
|
||||
condition: this.condition
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
sameAs: function(sourceID, line, condition) {
|
||||
return this.sourceID === sourceID &&
|
||||
this.line === line &&
|
||||
this.condition === condition;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Breakpoint = Breakpoint;
|
|
@ -36,7 +36,6 @@ function getSession(debuggerPort) {
|
|||
session.on('close', function () {
|
||||
sessions[debuggerPort] = null;
|
||||
});
|
||||
session.attach();
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var events = require('events'),
|
||||
debugr = require('./debugger');
|
||||
debugr = require('./debugger'),
|
||||
Breakpoint = require('./Breakpoint').Breakpoint;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// exports
|
||||
|
@ -7,6 +8,7 @@ var events = require('events'),
|
|||
exports.create = function(debuggerPort, config) {
|
||||
var debug = null,
|
||||
conn = null,
|
||||
attachedToDebugger = false,
|
||||
//map from sourceID:lineNumber to breakpoint
|
||||
breakpoints = {},
|
||||
//map from sourceID to filename
|
||||
|
@ -250,18 +252,28 @@ exports.create = function(debuggerPort, config) {
|
|||
debug.request('listbreakpoints', {},
|
||||
function(msg) {
|
||||
msg.body.breakpoints.forEach(function(bp) {
|
||||
var data;
|
||||
var data = {
|
||||
sourceID: null,
|
||||
url: null,
|
||||
line: bp.line + 1,
|
||||
enabled: bp.active,
|
||||
condition: bp.condition,
|
||||
number: bp.number
|
||||
};
|
||||
|
||||
if (bp.type === 'scriptId') {
|
||||
data = {
|
||||
sourceID: bp.script_id,
|
||||
url: sourceIDs[bp.script_id].url,
|
||||
line: bp.line + 1,
|
||||
enabled: bp.active,
|
||||
condition: bp.condition,
|
||||
number: bp.number
|
||||
};
|
||||
breakpoints[bp.script_id + ':' + (bp.line + 1)] = data;
|
||||
sendEvent('restoredBreakpoint', data);
|
||||
data.sourceID = bp.script_id;
|
||||
data.url = sourceIDs[bp.script_id].url;
|
||||
} else if (bp.type == 'scriptName') {
|
||||
data.url = bp.script_name;
|
||||
if (bp.actual_locations && bp.actual_locations.length > 0)
|
||||
data.sourceID = bp.actual_locations[0].script_id;
|
||||
}
|
||||
|
||||
if (data.sourceID !== null || data.url !== null) {
|
||||
var bpObj = new Breakpoint(data);
|
||||
breakpoints[bpObj.key] = bpObj;
|
||||
sendEvent('restoredBreakpoint', bpObj);
|
||||
}
|
||||
});
|
||||
if (!msg.running) {
|
||||
|
@ -271,6 +283,47 @@ exports.create = function(debuggerPort, config) {
|
|||
});
|
||||
}
|
||||
|
||||
function listBreakpointsToRestore(breakpointsAlreadySet) {
|
||||
var breakpointArray = Object.keys(breakpoints).map(function(k) {
|
||||
return breakpoints[k];
|
||||
});
|
||||
|
||||
return breakpointArray.filter(function(breakpoint) {
|
||||
function sameAsV8Breakpoint(v8data) {
|
||||
return breakpoint.sameAs(
|
||||
v8data.script_id,
|
||||
v8data.line + 1,
|
||||
v8data.condition);
|
||||
}
|
||||
|
||||
return !breakpointsAlreadySet.some(sameAsV8Breakpoint);
|
||||
});
|
||||
}
|
||||
|
||||
function restoreSessionBreakpoints(callback) {
|
||||
if (Object.keys(breakpoints).length < 1) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
debug.request('listbreakpoints', {}, function(msg) {
|
||||
var breakpointsToRestore = listBreakpointsToRestore(msg.body.breakpoints);
|
||||
|
||||
function restoreNext() {
|
||||
if (breakpointsToRestore.length < 1) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var bp = breakpointsToRestore.shift();
|
||||
var req = bp.createRequest();
|
||||
debug.request('setbreakpoint', req, restoreNext);
|
||||
}
|
||||
|
||||
restoreNext();
|
||||
});
|
||||
}
|
||||
|
||||
return Object.create(events.EventEmitter.prototype, {
|
||||
attach: {
|
||||
value: function()
|
||||
|
@ -286,10 +339,12 @@ exports.create = function(debuggerPort, config) {
|
|||
}
|
||||
};
|
||||
sendEvent('debuggerWasDisabled');
|
||||
self.close();
|
||||
// Do not close the session - keep it for debugee restart
|
||||
// self.close();
|
||||
attachedToDebugger = false;
|
||||
});
|
||||
debug.on('connect', function() {
|
||||
browserConnected();
|
||||
restoreSessionBreakpoints(browserConnected);
|
||||
});
|
||||
debug.on('exception', function(msg) {
|
||||
breakEvent(msg);
|
||||
|
@ -314,6 +369,8 @@ exports.create = function(debuggerPort, config) {
|
|||
};
|
||||
sendEvent('addConsoleMessage', data);
|
||||
});
|
||||
|
||||
attachedToDebugger = true;
|
||||
}
|
||||
},
|
||||
close: {
|
||||
|
@ -567,15 +624,15 @@ exports.create = function(debuggerPort, config) {
|
|||
handleResponse = function(msg) {
|
||||
if (msg.success) {
|
||||
var b = msg.body;
|
||||
breakpoints[b.script_id + ':' + (b.line + 1)] = {
|
||||
var breakpoint = new Breakpoint({
|
||||
sourceID: b.script_id,
|
||||
url: sourceIDs[b.script_id].url,
|
||||
line: b.line + 1,
|
||||
enabled: enabled,
|
||||
condition: condition,
|
||||
number: b.breakpoint
|
||||
};
|
||||
b.breakpoint;
|
||||
});
|
||||
breakpoints[breakpoint.key] = breakpoint;
|
||||
var data = { success: true, actualLineNumber: b.line + 1 };
|
||||
sendResponse(seq, true, data);
|
||||
}
|
||||
|
@ -945,6 +1002,8 @@ exports.create = function(debuggerPort, config) {
|
|||
},
|
||||
join: {
|
||||
value: function(ws_connection) {
|
||||
if (!attachedToDebugger)
|
||||
this.attach();
|
||||
var self = this;
|
||||
conn = ws_connection;
|
||||
conn.on('message', function(data) {
|
||||
|
|
|
@ -14,5 +14,12 @@
|
|||
"dependencies": {
|
||||
"socket.io": "~0.8.2",
|
||||
"paperboy": "~0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "latest",
|
||||
"chai": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
var expect = require('chai').expect,
|
||||
Breakpoint = require('../lib/Breakpoint').Breakpoint;
|
||||
|
||||
describe('Breakpoint', function() {
|
||||
describe('key', function() {
|
||||
it('should return scriptID:line', function() {
|
||||
var bp = aBreakpoint({sourceID: 10, line: 20});
|
||||
expect(bp.key).to.equal('10:20');
|
||||
})
|
||||
});
|
||||
|
||||
describe('createRequest()', function() {
|
||||
it('should create "script" type request', function() {
|
||||
var bp = aBreakpoint({
|
||||
url: '/script/url',
|
||||
line: 1,
|
||||
enabled: true,
|
||||
condition: 'a-condition'
|
||||
});
|
||||
|
||||
var request = bp.createRequest();
|
||||
|
||||
expect(request.arguments).to.deep.equal({
|
||||
type: 'script',
|
||||
target: '/script/url',
|
||||
line: 0, // V8 use zero-based line numbers
|
||||
enabled: true,
|
||||
condition: 'a-condition'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sameAs()', function() {
|
||||
var aLineNumber = 1;
|
||||
var anotherLineNumber = 2;
|
||||
var bp;
|
||||
|
||||
beforeEach(function() {
|
||||
bp = aBreakpoint({
|
||||
sourceID: 'a-source-id',
|
||||
line: aLineNumber,
|
||||
condition: 'a-condition'
|
||||
});
|
||||
})
|
||||
|
||||
it('returns true for matching breakpoint', function() {
|
||||
var result = bp.sameAs('a-source-id', aLineNumber, 'a-condition');
|
||||
expect(result).to.equal(true);
|
||||
})
|
||||
|
||||
it('returns false for different source id', function() {
|
||||
var result = bp.sameAs('another-source-id', aLineNumber, 'a-condition')
|
||||
expect(result).to.equal(false);
|
||||
})
|
||||
|
||||
it('returns false for different line', function() {
|
||||
var result = bp.sameAs('a-source-id', anotherLineNumber, 'a-condition')
|
||||
expect(result).to.equal(false);
|
||||
})
|
||||
|
||||
it('returns false for different condition', function() {
|
||||
var result = bp.sameAs('a-source-id', aLineNumber, 'another-condition')
|
||||
expect(result).to.equal(false);
|
||||
})
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
function aBreakpoint(props) {
|
||||
var val = {
|
||||
sourceID: 100, // arbitrary number
|
||||
url: 'http://some/script/url',
|
||||
line: 10, // arbitrary number
|
||||
enabled: true,
|
||||
condition: null,
|
||||
number: 1 // arbitrary number
|
||||
};
|
||||
for (var p in props) {
|
||||
val[p] = props[p];
|
||||
}
|
||||
|
||||
return new Breakpoint(props);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
--require chai
|
Загрузка…
Ссылка в новой задаче