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

Commit 61846c5

Browse files
author
Matt Loring
committed
Add support for log points
1 parent 5a39240 commit 61846c5

File tree

4 files changed

+148
-12
lines changed

4 files changed

+148
-12
lines changed

lib/debuglet.js

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ var assert = require('assert');
3232

3333
var NODE_VERSION_MESSAGE = 'Node.js version not supported. Node.js 5.2.0 and ' +
3434
' versions older than 0.12 are not supported.';
35-
var BREAKPOINT_ACTION_MESSAGE = 'The only currently supported breakpoint action' +
36-
' is CAPTURE.';
35+
var BREAKPOINT_ACTION_MESSAGE = 'The only currently supported breakpoint actions' +
36+
' are CAPTURE and LOG.';
3737

3838
module.exports = Debuglet;
3939

@@ -219,7 +219,7 @@ Debuglet.prototype.scheduleBreakpointFetch_ = function(seconds) {
219219
}
220220
var bps = (body.breakpoints || []).filter(function(bp) {
221221
var action = bp.action || 'CAPTURE';
222-
if (action !== 'CAPTURE') {
222+
if (action !== 'CAPTURE' && action !== 'LOG') {
223223
that.logger_.warn('Found breakpoint with invalid action:', action);
224224
bp.status = new StatusMessage(StatusMessage.UNSPECIFIED,
225225
BREAKPOINT_ACTION_MESSAGE, true);
@@ -333,15 +333,28 @@ Debuglet.prototype.addBreakpoint_ = function(breakpoint, cb) {
333333
that.logger_.info('\tsuccessfully added breakpoint ' + breakpoint.id);
334334
that.activeBreakpointMap_[breakpoint.id] = breakpoint;
335335

336-
that.v8debug_.wait(breakpoint, function(err) {
336+
that.v8debug_.wait(breakpoint, function waitHandler(err) {
337337
if (err) {
338338
that.logger_.error(err);
339339
cb(err);
340340
return;
341341
}
342342

343343
that.logger_.info('Breakpoint hit!: ' + breakpoint.id);
344-
that.completeBreakpoint_(breakpoint);
344+
if (breakpoint.action === 'LOG') {
345+
var message = Debuglet.format(breakpoint.logMessageFormat,
346+
breakpoint.evaluatedExpressions.map(function(v) {
347+
return v.value;
348+
}));
349+
console.log(message);
350+
if (!that.completedBreakpointMap_[breakpoint.id]) {
351+
setTimeout(function() {
352+
that.v8debug_.wait(breakpoint, waitHandler);
353+
}, 500);
354+
}
355+
} else {
356+
that.completeBreakpoint_(breakpoint);
357+
}
345358
});
346359
});
347360
};
@@ -434,3 +447,53 @@ Debuglet.mapSubtract = function mapSubtract(A, B) {
434447
return removed;
435448
};
436449

450+
/**
451+
* Formats the message base with placeholders `$0`, `$1`, etc
452+
* by substituting the provided expressions. If more expressions
453+
* are given than placeholders extra expressions are dropped.
454+
*/
455+
Debuglet.format = function(base, exprs) {
456+
var tokens = Debuglet._tokenize(base, exprs.length);
457+
for (var i = 0; i < tokens.length; i++) {
458+
if (!tokens[i].v) {
459+
continue;
460+
}
461+
if (tokens[i].v === '$$') {
462+
tokens[i] = '$';
463+
continue;
464+
}
465+
for (var j = 0; j < exprs.length; j++) {
466+
if (tokens[i].v === '$' + j) {
467+
tokens[i] = exprs[j];
468+
break;
469+
}
470+
}
471+
}
472+
return tokens.join('');
473+
};
474+
475+
Debuglet._tokenize = function(base, exprLength) {
476+
var acc = Debuglet._delimit(base, '$$');
477+
for (var i = exprLength - 1; i >= 0; i--) {
478+
var newAcc = [];
479+
for (var j = 0; j < acc.length; j++) {
480+
if (acc[j].v) {
481+
newAcc.push(acc[j]);
482+
} else {
483+
newAcc.push.apply(newAcc, Debuglet._delimit(acc[j], '$' + i));
484+
}
485+
}
486+
acc = newAcc;
487+
}
488+
return acc;
489+
};
490+
491+
Debuglet._delimit = function(source, delim) {
492+
var pieces = source.split(delim);
493+
var dest = [];
494+
dest.push(pieces[0]);
495+
for (var i = 1; i < pieces.length; i++) {
496+
dest.push({ v: delim }, pieces[i]);
497+
}
498+
return dest;
499+
};

lib/v8debugapi.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
/** @const */ var state = require('./state.js');
2424
/** @const */ var logModule = require('@google/cloud-diagnostics-common').logger;
2525
/** @const */ var apiclasses = require('./apiclasses.js');
26+
/** @const */ var _ = require('lodash');
2627
/** @const */ var StatusMessage = apiclasses.StatusMessage;
2728

2829
/** @const */ var messages = {
@@ -484,7 +485,14 @@ module.exports.create = function(logger_, config_, fileStats_) {
484485
}
485486
}
486487
}
487-
var captured = state.capture(execState, breakpoint.expressions, config);
488+
var localConfig;
489+
if (breakpoint.action === 'LOG') {
490+
localConfig = { capture: { maxFrames: 0 } };
491+
_.defaultsDeep(localConfig, config);
492+
} else {
493+
localConfig = config;
494+
}
495+
var captured = state.capture(execState, breakpoint.expressions, localConfig);
488496
breakpoint.stackFrames = captured.stackFrames;
489497
breakpoint.variableTable = captured.variableTable;
490498
breakpoint.evaluatedExpressions =

test/e2e/test.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var SCOPES = [
3636
];
3737

3838
var agent;
39+
var transcript = '';
3940

4041
function apiRequest(authClient, url, method, body) {
4142
method = method || 'GET';
@@ -131,6 +132,45 @@ function runTest() {
131132
console.log('-- delete results', deleteResults);
132133
return debuggee;
133134
})
135+
.then(function(debuggee) {
136+
// Set a breakpoint
137+
console.log('-- setting a logpoint');
138+
var promise = setBreakpoint(debuggee, {
139+
id: 'breakpoint-1',
140+
location: {path: 'test.js', line: 5},
141+
condition: 'n === 10',
142+
action: 'LOG',
143+
expressions: ['n'],
144+
log_message_format: 'n is: $0'
145+
});
146+
// I don't know what I am doing. There is a better way to write the
147+
// following using promises.
148+
var result = promise.then(function(body) {
149+
console.log('-- resolution of setBreakpoint', body);
150+
assert.ok(body.breakpoint, 'should have set a breakpoint');
151+
var breakpoint = body.breakpoint;
152+
assert.ok(breakpoint.id, 'breakpoint should have an id');
153+
assert.ok(breakpoint.location, 'breakpoint should have a location');
154+
assert.strictEqual(breakpoint.location.path, 'test.js');
155+
return { debuggee: debuggee, breakpoint: breakpoint };
156+
});
157+
return result;
158+
})
159+
.then(function(result) {
160+
assert.ok(result.breakpoint);
161+
assert.ok(result.debuggee);
162+
console.log('-- waiting a bit before running fib');
163+
return Q.delay(10 * 1000).then(function() { return result; });
164+
})
165+
.then(function(result) {
166+
console.log('-- waiting before checking if the log was written');
167+
return Q.delay(10 * 1000).then(function() { return result; });
168+
})
169+
.then(function(result) {
170+
assert(transcript.indexOf('n is: 10') !== -1);
171+
deleteBreakpoint(result.debuggee, result.breakpoint).then();
172+
return result.debuggee;
173+
})
134174
.then(function(debuggee) {
135175
// Set a breakpoint
136176
console.log('-- setting a breakpoint');
@@ -183,6 +223,8 @@ function runTest() {
183223
});
184224
assert.ok(arg, 'should find the n argument');
185225
assert.strictEqual(arg.value, '10');
226+
console.log('-- checking log point was hit again');
227+
assert.ok(transcript.split('n is: 10').length > 4);
186228
console.log('Test passed');
187229
process.exit(0);
188230
})
@@ -198,6 +240,7 @@ function runTest() {
198240
}
199241

200242
if (cluster.isMaster) {
243+
cluster.setupMaster({ silent: true });
201244
var handler = function(m) {
202245
assert.ok(m.private_, 'debuglet has initialized');
203246
var debuglet = m.private_;
@@ -212,8 +255,13 @@ if (cluster.isMaster) {
212255
agent = m;
213256
}
214257
};
258+
var stdoutHandler = function(chunk) {
259+
transcript += chunk;
260+
};
215261
for (var i = 0; i < 3; i++) {
216-
cluster.fork().on('message', handler);
262+
var worker = cluster.fork();
263+
worker.on('message', handler);
264+
worker.process.stdout.on('data', stdoutHandler);
217265
}
218266
runTest();
219267
} else {

test/standalone/test-debuglet.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ var bp = {
3535
action: 'CAPTURE',
3636
location: { path: 'fixtures/foo.js', line: 2 }
3737
};
38-
var logBp = {
38+
var errorBp = {
3939
id: 'testLog',
40-
action: 'LOG',
40+
action: 'FOO',
4141
location: { path: 'fixtures/foo.js', line: 2 }
4242
};
4343

@@ -166,12 +166,12 @@ describe(__filename, function(){
166166
})
167167
.get(BPS_PATH + '?success_on_timeout=true')
168168
.reply(200, {
169-
breakpoints: [bp, logBp]
169+
breakpoints: [bp, errorBp]
170170
})
171-
.put(BPS_PATH + '/' + logBp.id, function(body) {
171+
.put(BPS_PATH + '/' + errorBp.id, function(body) {
172172
var status = body.breakpoint.status;
173173
return status.isError &&
174-
status.description.format.indexOf('action is CAPTURE') !== -1;
174+
status.description.format.indexOf('actions are CAPTURE') !== -1;
175175
})
176176
.reply(200);
177177

@@ -280,5 +280,22 @@ describe(__filename, function(){
280280
assert.deepEqual(Debuglet.mapSubtract({}, b), []);
281281
});
282282
});
283+
284+
describe('format', function() {
285+
it('should be correct', function() {
286+
assert.deepEqual(Debuglet.format('hi', [5]), 'hi');
287+
assert.deepEqual(Debuglet.format('hi $0', [5]), 'hi 5');
288+
assert.deepEqual(Debuglet.format('hi $0 $1', [5, 'there']), 'hi 5 there');
289+
assert.deepEqual(Debuglet.format('hi $0 $1', [5]), 'hi 5 $1');
290+
assert.deepEqual(Debuglet.format('hi $0 $1 $0', [5]), 'hi 5 $1 5');
291+
assert.deepEqual(Debuglet.format('hi $$', [5]), 'hi $');
292+
assert.deepEqual(Debuglet.format('hi $$0', [5]), 'hi $0');
293+
assert.deepEqual(Debuglet.format('hi $00', [5]), 'hi 50');
294+
assert.deepEqual(Debuglet.format('hi $0', ['$1', 5]), 'hi $1');
295+
assert.deepEqual(Debuglet.format('hi $11',
296+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd']
297+
), 'hi b');
298+
});
299+
});
283300
});
284301

0 commit comments

Comments
 (0)