Browse Source

Merge branch 'debugger-bytecode-view'

Add a very basic bytecode view to the debugger.
pull/156/head
Sami Vaarala 10 years ago
parent
commit
e467d0cae7
  1. 191
      debugger/duk_debug.js
  2. 631
      debugger/duk_opcodes.yaml
  3. 3
      debugger/package.json
  4. 5
      debugger/static/index.html
  5. 8
      debugger/static/style.css
  6. 18
      debugger/static/webui.js
  7. 21
      doc/debugger.rst
  8. 55
      src/duk_debugger.c
  9. 1
      src/duk_debugger.h

191
debugger/duk_debug.js

@ -28,6 +28,7 @@ var readline = require('readline');
var sprintf = require('sprintf').sprintf;
var utf8 = require('utf8');
var wrench = require('wrench'); // https://github.com/ryanmcgrath/wrench-js
var yaml = require('yamljs');
// Command line options (defaults here, overwritten if necessary)
var optTargetHost = '127.0.0.1';
@ -69,6 +70,7 @@ var CMD_GETLOCALS = 0x1d;
var CMD_EVAL = 0x1e;
var CMD_DETACH = 0x1f;
var CMD_DUMPHEAP = 0x20;
var CMD_GETBYTECODE = 0x21;
// Errors
var ERR_UNKNOWN = 0x00;
@ -138,6 +140,9 @@ var dukClassNames = [
'Thread'
];
// Bytecode opcode/extraop metadata
var dukOpcodes = yaml.load('duk_opcodes.yaml')
/*
* Miscellaneous helpers
*/
@ -976,6 +981,97 @@ function Debugger() {
}
Debugger.prototype = events.EventEmitter.prototype;
Debugger.prototype.decodeBytecodeFromBuffer = function (buf, consts, funcs) {
var i, j, n, m, ins, pc;
var res = [];
var op, str, args, comments;
// XXX: add constants inline to preformatted output (e.g. for strings,
// add a short escaped snippet as a comment on the line after the
// compact argument list).
for (i = 0, n = buf.length; i < n; i += 4) {
pc = i / 4;
// shift forces unsigned
if (this.endianness === 'little') {
ins = buf.readInt32LE(i) >>> 0;
} else {
ins = buf.readInt32BE(i) >>> 0;
}
op = dukOpcodes.opcodes[ins & 0x3f];
if (op.extra) {
op = dukOpcodes.extra[(ins >> 6) & 0xff];
}
args = [];
comments = [];
if (op.args) {
for (j = 0, m = op.args.length; j < m; j++) {
switch(op.args[j]) {
case 'A_R': args.push('r' + ((ins >>> 6) & 0xff)); break;
case 'A_RI': args.push('r' + ((ins >>> 6) & 0xff) + '(indirect)'); break;
case 'A_C': args.push('c' + ((ins >>> 6) & 0xff)); break;
case 'A_H': args.push('0x' + ((ins >>> 6) & 0xff).toString(16)); break;
case 'A_I': args.push(((ins >>> 6) & 0xff).toString(10)); break;
case 'A_B': args.push(((ins >>> 6) & 0xff) ? 'true' : 'false'); break;
case 'B_RC': args.push((ins & (1 << 22) ? 'c' : 'r') + ((ins >>> 14) & 0x0ff)); break;
case 'B_R': args.push('r' + ((ins >>> 14) & 0x1ff)); break;
case 'B_RI': args.push('r' + ((ins >>> 14) & 0x1ff) + '(indirect)'); break;
case 'B_C': args.push('c' + ((ins >>> 14) & 0x1ff)); break;
case 'B_H': args.push('0x' + ((ins >>> 14) & 0x1ff).toString(16)); break;
case 'B_I': args.push(((ins >>> 14) & 0x1ff).toString(10)); break;
case 'C_RC': args.push((ins & (1 << 31) ? 'c' : 'r') + ((ins >>> 23) & 0x0ff)); break;
case 'C_R': args.push('r' + ((ins >>> 23) & 0x1ff)); break;
case 'C_RI': args.push('r' + ((ins >>> 23) & 0x1ff) + '(indirect)'); break;
case 'C_C': args.push('c' + ((ins >>> 23) & 0x1ff)); break;
case 'C_H': args.push('0x' + ((ins >>> 23) & 0x1ff).toString(16)); break;
case 'C_I': args.push(((ins >>> 23) & 0x1ff).toString(10)); break;
case 'BC_R': args.push('r' + ((ins >>> 14) & 0x3ffff)); break;
case 'BC_C': args.push('c' + ((ins >>> 14) & 0x3ffff)); break;
case 'BC_H': args.push('0x' + ((ins >>> 14) & 0x3ffff).toString(16)); break;
case 'BC_I': args.push(((ins >>> 14) & 0x3ffff).toString(10)); break;
case 'ABC_H': args.push(((ins >>> 6) & 0x03ffffff).toString(16)); break;
case 'ABC_I': args.push(((ins >>> 6) & 0x03ffffff).toString(10)); break;
case 'BC_LDINT': args.push(((ins >>> 14) & 0x3ffff) - (1 << 17)); break;
case 'BC_LDINTX': args.push(((ins >>> 14) & 0x3ffff) - 0); break; // no bias in LDINTX
case 'ABC_JUMP': {
var pc_add = ((ins >>> 6) & 0x03ffffff) - (1 << 25) + 1; // pc is preincremented before adding
var pc_dst = pc + pc_add;
args.push(pc_dst + ' (' + (pc_add >= 0 ? '+' : '') + pc_add + ')');
break;
}
default: args.push('?'); break;
}
}
}
if (op.flags) {
for (j = 0, m = op.flags.length; j < m; j++) {
if (ins & op.flags[j].mask) {
comments.push(op.flags[j].name);
}
}
}
if (args.length > 0) {
str = sprintf('%05d %08x %-10s %s', pc, ins, op.name, args.join(', '));
} else {
str = sprintf('%05d %08x %-10s', pc, ins, op.name);
}
if (comments.length > 0) {
str = sprintf('%-40s ; %s', str, comments.join(', '));
}
res.push({
str: str,
ins: ins
});
}
return res;
};
Debugger.prototype.uiMessage = function (type, val) {
var msg;
if (typeof type === 'object') {
@ -1129,32 +1225,32 @@ Debugger.prototype.sendGetCallStackRequest = function () {
});
};
Debugger.prototype.sendStepInto = function () {
Debugger.prototype.sendStepIntoRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_STEPINTO, DVAL_EOM ]);
};
Debugger.prototype.sendStepOver = function () {
Debugger.prototype.sendStepOverRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_STEPOVER, DVAL_EOM ]);
};
Debugger.prototype.sendStepOut = function () {
Debugger.prototype.sendStepOutRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_STEPOUT, DVAL_EOM ]);
};
Debugger.prototype.sendPause = function () {
Debugger.prototype.sendPauseRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_PAUSE, DVAL_EOM ]);
};
Debugger.prototype.sendResume = function () {
Debugger.prototype.sendResumeRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_RESUME, DVAL_EOM ]);
};
Debugger.prototype.sendEval = function (evalInput) {
Debugger.prototype.sendEvalRequest = function (evalInput) {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_EVAL, evalInput, DVAL_EOM ]).then(function (msg) {
return { error: msg[1] === 1 /*error*/, value: msg[2] };
@ -1166,7 +1262,7 @@ Debugger.prototype.sendDetachRequest = function () {
return this.sendRequest([ DVAL_REQ, CMD_DETACH, DVAL_EOM ]);
};
Debugger.prototype.sendDumpHeap = function () {
Debugger.prototype.sendDumpHeapRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_DUMPHEAP, DVAL_EOM ]).then(function (msg) {
@ -1234,8 +1330,69 @@ Debugger.prototype.sendDumpHeap = function () {
});
};
Debugger.prototype.sendGetBytecodeRequest = function () {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_GETBYTECODE, DVAL_EOM ]).then(function (msg) {
var idx = 1;
var nconst;
var nfunc;
var val;
var buf;
var i, n;
var consts = [];
var funcs = [];
var bcode;
var preformatted;
var ret;
//console.log(JSON.stringify(msg));
nconst = msg[idx++];
for (i = 0; i < nconst; i++) {
val = msg[idx++];
consts.push(val);
}
nfunc = msg[idx++];
for (i = 0; i < nfunc; i++) {
val = msg[idx++];
funcs.push(val);
}
val = msg[idx++];
// Right now bytecode is a string containing a direct dump of the
// bytecode in target endianness. Decode here so that the web UI
// doesn't need to.
buf = new Buffer(val.length);
writeDebugStringToBuffer(val, buf, 0);
bcode = _this.decodeBytecodeFromBuffer(buf, consts, funcs);
preformatted = [];
consts.forEach(function (v, i) {
preformatted.push('; c' + i + ' ' + JSON.stringify(v));
});
preformatted.push('');
bcode.forEach(function (v) {
preformatted.push(v.str);
});
preformatted = preformatted.join('\n') + '\n';
ret = {
"constants": consts,
"functions": funcs,
"bytecode": bcode,
"preformatted": preformatted
};
return ret;
});
};
Debugger.prototype.changeBreakpoint = function (fileName, lineNumber, mode) {
var _this = this;
return this.sendRequest([ DVAL_REQ, CMD_LISTBREAK, DVAL_EOM ]).then(function (msg) {
var i, n;
var breakpts = [];
@ -1592,7 +1749,7 @@ DebugWebServer.prototype.handleSourceListPost = function (req, res) {
DebugWebServer.prototype.handleHeapDumpGet = function (req, res) {
console.log('Heap dump get');
this.dbg.sendDumpHeap().then(function (val) {
this.dbg.sendDumpHeapRequest().then(function (val) {
res.header('Content-Type', 'application/json');
//res.status(200).json(val);
res.status(200).send(JSON.stringify(val, null, 4));
@ -1709,30 +1866,30 @@ DebugWebServer.prototype.handleNewSocketIoConnection = function (socket) {
});
socket.on('stepinto', function (msg) {
_this.dbg.sendStepInto();
_this.dbg.sendStepIntoRequest();
});
socket.on('stepover', function (msg) {
_this.dbg.sendStepOver();
_this.dbg.sendStepOverRequest();
});
socket.on('stepout', function (msg) {
_this.dbg.sendStepOut();
_this.dbg.sendStepOutRequest();
});
socket.on('pause', function (msg) {
_this.dbg.sendPause();
_this.dbg.sendPauseRequest();
});
socket.on('resume', function (msg) {
_this.dbg.sendResume();
_this.dbg.sendResumeRequest();
});
socket.on('eval', function (msg) {
// msg.input is a proper Unicode strings here, and needs to be
// converted into a protocol string (U+0000...U+00FF).
var input = stringToDebugString(msg.input);
_this.dbg.sendEval(input).then(function (v) {
_this.dbg.sendEvalRequest(input).then(function (v) {
socket.emit('eval-result', { error: v.error, result: prettyUiDebugValue(v.value, EVAL_CLIPLEN) });
});
@ -1793,6 +1950,12 @@ DebugWebServer.prototype.handleNewSocketIoConnection = function (socket) {
_this.dbg.changeBreakpoint(null, null, 'deleteall');
});
socket.on('get-bytecode', function (msg) {
_this.dbg.sendGetBytecodeRequest().then(function (res) {
socket.emit('bytecode', res);
});
});
// Resend all debugger state for new client
this.cachedJson = {}; // clear client state cache
this.emitBasicInfo();

631
debugger/duk_opcodes.yaml

@ -0,0 +1,631 @@
# Duktape opcode metadata for debugger.
# - See duk_debug.js for the argument formats (A_R etc).
# - Flag bits are for the whole instruction as a 32-bit integer,
# they are not field shifted
# XXX: Need to rework after performance optimization merges which
# reworked several opcodes.
opcodes:
- name: LDREG
args:
- A_R
- BC_R
- name: STREG
args:
- A_R
- BC_R
- name: LDCONST
args:
- A_R
- BC_C
- name: LDINT
args:
- A_R
- BC_LDINT
- name: LDINTX
args:
- A_R
- BC_LDINTX
- name: MPUTOBJ
args:
- A_R
- B_R
- C_I
- name: MPUTOBJI
args:
- A_R
- B_RI
- C_I
- name: MPUTARR
args:
- A_R
- B_R
- C_I
- name: MPUTARRI
args:
- A_R
- B_RI
- C_I
- name: NEW
args:
- B_R
- C_I
- name: NEWI
args:
- B_RI
- C_I
- name: REGEXP
args:
- A_R
- B_RC
- C_RC
- name: CSREG
args:
- A_R
- B_R
- name: CSREGI
args:
- A_RI
- B_R
- name: GETVAR
args:
- A_R
- BC_C
- name: PUTVAR
args:
- A_R
- BC_C
- name: DECLVAR
args:
- A_H
- B_RC
- C_RC
flags:
- mask: 0x40
name: writable
- mask: 0x80
name: enumerable
- mask: 0x100
name: configurable
- mask: 0x200
name: accessor
- mask: 0x400
name: undef_value
- mask: 0x800
name: func_decl
- name: DELVAR
args:
- A_R
- B_RC
- name: CSVAR
args:
- A_R
- B_RC
- name: CSVARI
args:
- A_RI
- B_RC
- name: CLOSURE
args:
- A_R
- BC_I
- name: GETPROP
args:
- A_R
- B_RC
- C_RC
- name: PUTPROP
args:
- A_R
- B_RC
- C_RC
- name: DELPROP
args:
- A_R
- B_R
- C_RC
- name: CSPROP
args:
- A_R
- B_R
- C_RC
- name: CSPROPI
args:
- A_RI
- B_R
- C_RC
- name: ADD
args:
- A_R
- B_RC
- C_RC
- name: SUB
args:
- A_R
- B_RC
- C_RC
- name: MUL
args:
- A_R
- B_RC
- C_RC
- name: DIV
args:
- A_R
- B_RC
- C_RC
- name: MOD
args:
- A_R
- B_RC
- C_RC
- name: BAND
args:
- A_R
- B_RC
- C_RC
- name: BOR
args:
- A_R
- B_RC
- C_RC
- name: BXOR
args:
- A_R
- B_RC
- C_RC
- name: BASL
args:
- A_R
- B_RC
- C_RC
- name: BLSR
args:
- A_R
- B_RC
- C_RC
- name: BASR
args:
- A_R
- B_RC
- C_RC
- name: BNOT
args:
- A_R
- B_RC
- name: LNOT
args:
- A_R
- B_RC
- name: EQ
args:
- A_R
- B_RC
- C_RC
- name: NEQ
args:
- A_R
- B_RC
- C_RC
- name: SEQ
args:
- A_R
- B_RC
- C_RC
- name: SNEQ
args:
- A_R
- B_RC
- C_RC
- name: GT
args:
- A_R
- B_RC
- C_RC
- name: GE
args:
- A_R
- B_RC
- C_RC
- name: LT
args:
- A_R
- B_RC
- C_RC
- name: LE
args:
- A_R
- B_RC
- C_RC
- name: IF
args:
- A_B
- B_RC
- name: INSTOF
args:
- A_R
- B_RC
- C_RC
- name: IN
args:
- A_R
- B_RC
- C_RC
- name: JUMP
args:
- ABC_JUMP
- name: RETURN
args:
- A_H
- B_RC
flags:
- mask: 0x40
name: fast_return
- mask: 0x80
name: have_retval
- name: CALL
args:
- A_H
- B_R
- C_I
flags:
- mask: 0x40
name: tailcall
- mask: 0x80
name: evalcall
- name: CALLI
args:
- A_H
- B_RI
- C_I
- name: LABEL
args:
- ABC_I
- name: ENDLABEL
args:
- ABC_I
- name: BREAK
args:
- ABC_I
- name: CONTINUE
args:
- ABC_I
- name: TRYCATCH
args:
- A_H
- B_R
- C_I # semantics depend on flags, so dump as integer
flags:
- mask: 0x40
name: have_catch
- mask: 0x80
name: have_finally
- mask: 0x100
name: catch_binding
- mask: 0x200
name: with_binding
- name: UNUSED59
- name: UNUSED60
- name: UNUSED61
- name: EXTRA
extra: true
- name: INVALID
args:
- ABC_I
extra:
- name: 'NOP'
- name: 'LDTHIS'
args:
- BC_R
- name: 'LDUNDEF'
args:
- BC_R
- name: 'LDNULL'
args:
- BC_R
- name: 'LDTRUE'
args:
- BC_R
- name: 'LDFALSE'
args:
- BC_R
- name: 'NEWOBJ'
args:
- BC_R
- name: 'NEWARR'
args:
- BC_R
- name: 'SETALEN'
args:
- B_R
- C_R
- name: 'TYPEOF'
args:
- B_R
- C_RC
- name: 'TYPEOFID'
args:
- B_R
- C_RC # maybe changed to C_C later
- name: 'TONUM'
args:
- B_R
- C_R
- name: 'INITENUM'
args:
- B_R
- C_R
- name: 'NEXTENUM'
args:
- B_R
- C_R
- name: 'INITSET'
args:
- B_R
- C_R
- name: 'INITSETI'
args:
- B_R
- C_RI
- name: 'INITGET'
args:
- B_R
- C_RI
- name: 'INITGETI'
args:
- B_R
- C_RI
- name: 'ENDTRY'
- name: 'ENDCATCH'
- name: 'ENDFIN'
- name: 'THROW'
args:
- B_R
- name: 'INVLHS'
- name: 'UNM'
args:
- B_R
- C_RC
- name: 'UNP'
args:
- B_R
- C_RC
- name: 'INC'
args:
- B_R
- C_RC
- name: 'DEC'
args:
- B_R
- C_RC
- name: 'DEBUGGER'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'
- name: 'UNKNOWN'

3
debugger/package.json

@ -19,7 +19,8 @@
"stream": "0.0.2",
"readline": "0.0.5",
"util": "~0.10.3",
"http": "0.0.0"
"http": "0.0.0",
"yamljs": "~0.2.1"
},
"main": "duk_debug.js"
}

5
debugger/static/index.html

@ -29,6 +29,7 @@
<button id="detach-button">Detach</button>
<button id="about-button">About</button>
<button id="heap-dump-download-button"><a id="heap-dump-download" href="/heapDump.json" target="_blank">Dump&#x00a0;heap</a></button>
<button id="show-bytecode-button">Show bytecode</button>
</div> <!-- #left-area -->
<div id="center-area">
@ -84,6 +85,10 @@ The debug server talks to the target device using the Duktape debug protocol
<p>Duktape debugger is currently <span style="font-weight: bold">experimental</span>.</p>
</div> <!-- #about-dialog -->
<div id="bytecode-dialog" title="Bytecode for current function">
<pre id="bytecode-preformatted"></pre>
</div>
<script src="jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="jquery-ui.min.js" type="text/javascript"></script>
<script src="socket.io-1.2.0.js" type="text/javascript"></script>

8
debugger/static/style.css

@ -354,6 +354,14 @@
margin: 10px 0 10px 0;
}
#bytecode-dialog p {
margin: 10px 0 10px 0;
}
#bytecode-dialog pre {
font: 14pt monospace;
color: #000000;
}
#eval {
color: #000000;
margin-top: 10px;

18
debugger/static/webui.js

@ -454,6 +454,11 @@ socket.on('getvar-result', function (msg) {
$('#var-output').text(msg.found ? msg.result : 'NOTFOUND');
});
socket.on('bytecode', function (msg) {
$('#bytecode-preformatted').text(msg.preformatted);
$('#bytecode-dialog').dialog('open');
});
$('#stepinto-button').click(function () {
socket.emit('stepinto', {});
});
@ -489,6 +494,10 @@ $('#about-button').click(function () {
$('#about-dialog').dialog('open');
});
$('#show-bytecode-button').click(function () {
socket.emit('get-bytecode', {});
});
function submitEval() {
socket.emit('eval', { input: $('#eval-input').val() });
@ -688,6 +697,15 @@ $(document).ready(function () {
height: 300
});
// Bytecode dialog
$('#bytecode-dialog').dialog({
autoOpen: false,
hide: 'fade', // puff
show: 'fade', // slide, puff
width: 1000,
height: 800
});
// http://diveintohtml5.info/storage.html
if (typeof localStorage !== 'undefined') {
if (localStorage.getItem('about-shown')) {

21
doc/debugger.rst

@ -1452,6 +1452,27 @@ dump of the heap state for analysis.
to implement a heap browser, and will probably be completed together with
some kind of UI.
GetBytecode request (0x21)
--------------------------
Format::
REQ <int: 0x21> EOM
REP <int: numconsts> (<tval: const>){numconsts}
<int: numfuncs> (<tval: func>){numfuncs}
<str: bytecode> EOM
Example::
REQ 33 EOM
REP 2 "foo" "bar" 0 "...bytecode..." EOM
Bytecode endianness is target specific so the debug client needs to get
target endianness and interpret the bytecode based on that.
.. note:: This command is somewhat incomplete at the moment and may be modified
once the best way to do this in the debugger UI has been figured out.
"debugger" statement
====================

55
src/duk_debugger.c

@ -1436,6 +1436,57 @@ DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) {
}
#endif /* DUK_USE_DEBUGGER_DUMPHEAP */
DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) {
duk_activation *act;
duk_hcompiledfunction *fun;
duk_size_t i, n;
duk_tval *tv;
duk_hobject **fn;
DUK_UNREF(heap);
DUK_D(DUK_DPRINT("debug command getbytecode"));
duk_debug_write_reply(thr);
if (thr->callstack_top == 0) {
fun = NULL;
} else {
act = thr->callstack + thr->callstack_top - 1;
fun = (duk_hcompiledfunction *) DUK_ACT_GET_FUNC(act);
if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun)) {
fun = NULL;
}
}
DUK_ASSERT(fun == NULL || DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) fun));
if (fun) {
n = DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(heap, fun);
duk_debug_write_int(thr, (int) n);
tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, fun);
for (i = 0; i < n; i++) {
duk_debug_write_tval(thr, tv);
tv++;
}
n = DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(heap, fun);
duk_debug_write_int(thr, (int) n);
fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, fun);
for (i = 0; i < n; i++) {
duk_debug_write_hobject(thr, *fn);
fn++;
}
duk_debug_write_string(thr,
(const char *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(heap, fun),
(duk_size_t) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(heap, fun));
} else {
duk_debug_write_int(thr, 0);
duk_debug_write_int(thr, 0);
duk_debug_write_cstring(thr, "");
}
duk_debug_write_eom(thr);
}
DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
duk_context *ctx = (duk_context *) thr;
duk_heap *heap;
@ -1516,6 +1567,10 @@ DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
break;
}
#endif /* DUK_USE_DEBUGGER_DUMPHEAP */
case DUK_DBG_CMD_GETBYTECODE: {
duk__debug_handle_get_bytecode(thr, heap);
break;
}
default: {
DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");

1
src/duk_debugger.h

@ -38,6 +38,7 @@
#define DUK_DBG_CMD_EVAL 0x1e
#define DUK_DBG_CMD_DETACH 0x1f
#define DUK_DBG_CMD_DUMPHEAP 0x20
#define DUK_DBG_CMD_GETBYTECODE 0x21
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap);

Loading…
Cancel
Save