Added breakpoint command support to the "g" command

This commit is contained in:
Jeff Parsons 2015-08-20 11:26:38 -07:00
commit 473d13e87a

View file

@ -3327,22 +3327,49 @@ if (DEBUGGER) {
* addBreakpoint(aBreak, dbgAddr, fTempBreak) * addBreakpoint(aBreak, dbgAddr, fTempBreak)
* *
* In case you haven't already figured this out, all our breakpoint commands use the address * In case you haven't already figured this out, all our breakpoint commands use the address
* to identify a breakpoint, not some randomly assigned breakpoint index like other debuggers; * to identify a breakpoint, not an incrementally assigned breakpoint index like other debuggers;
* see doBreak() for details. * see doBreak() for details.
* *
* This has a few implications, one being that you CANNOT set more than one kind of breakpoint * This has a few implications, one being that you CANNOT set more than one kind of breakpoint
* on a single address. In practice, that's rarely a problem, because you can almost always * on a single address. In practice, that's rarely a problem, because you can almost always set
* set a different breakpoint on a neighboring address. * a different breakpoint on a neighboring address.
* *
* Also, there is one exception to the "one address, one breakpoint" rule, and that involves * Also, there is one exception to the "one address, one breakpoint" rule, and that involves
* temporary breakpoints (ie, one-time execution breakpoints that either a "p" or "g" command * temporary breakpoints (ie, one-time execution breakpoints that either a "p" or "g" command
* can create to step over a chunk of code). Those breakpoints automatically clear themselves, * may create to step over a chunk of code). Those breakpoints automatically clear themselves,
* so there usually isn't any need to refer to them using breakpoint commands. * so there usually isn't any need to refer to them using breakpoint commands.
* *
* TODO: Consider supporting the more "traditional" breakpoint index syntax; the current * TODO: Consider supporting the more "traditional" breakpoint index syntax; the current
* address-based syntax was implemented solely for expediency and orthogonality. At the same * address-based syntax was implemented solely for expediency and consistency. At the same time,
* time, also consider a more WDEB386-like syntax, where "br" is used to set a variety of * also consider a more WDEB386-like syntax, where "br" is used to set a variety of access
* access breakpoints using modifiers like "r1", "r2", "w1", "w2, etc. * breakpoints using modifiers like "r1", "r2", "w1", "w2, etc.
*
* EXAMPLE: Here's an example of our powerful new breakpoint command capabilities:
*
* bp 0397:022B "?'GlobalAlloc(wFlags:[ss:sp+8],dwBytes:[ss:sp+6][ss:sp+4])';g [ss:sp+2]:[ss:sp] '?ax;if ax'"
*
* The above breakpoint will display a pleasing "GlobalAlloc()" string containing the current
* stack parameters, and will briefly stop execution on the return to print the result in AX,
* halting the CPU whenever AX is zero (the default behavior of "if" whenever the expression is
* false is to look for an "else" and automatically halt when there is no "else").
*
* How do you figure out where the code for GlobalAlloc is in the first place? You need to have
* BACKTRACK support enabled (which currently means running the non-COMPILED version), so that as
* the Disk component loads disk images, it will automatically extract symbolic information from all
* "NE" (New Executable) binaries on those disks, which the Debugger's "di" command can then search
* for you; eg:
*
* ## di globalalloc
* GLOBALALLOC: KRNL386.EXE 0001:022B len 0xC570
*
* And then you just need to do a bit more sleuthing to find the right CODE segment. If you have
* WDEB386.EXE loaded inside the machine, it's a little easier, because WDEB386 displays notifications
* like:
*
* KERNEL!undefined code(0001)=#0397 len 0000C580
*
* as segments are being loaded. TODO: Consider adding our own INT 0x41 support, so that even when
* WDEB386.EXE isn't loaded, the PCjs Debugger can provide some of those same notifications.
* *
* @this {Debugger} * @this {Debugger}
* @param {Array} aBreak * @param {Array} aBreak
@ -3599,7 +3626,7 @@ if (DEBUGGER) {
this.findBreakpoint(aBreak, dbgAddrBreak, true, true); this.findBreakpoint(aBreak, dbgAddrBreak, true, true);
fTempBreak = true; fTempBreak = true;
} }
else if (a = dbgAddrBreak.aCmds) { if (a = dbgAddrBreak.aCmds) {
/* /*
* When one or more commands are attached to a breakpoint, we don't halt by default. * When one or more commands are attached to a breakpoint, we don't halt by default.
* Instead, we set fBreak to true only if, at the completion of all the commands, the * Instead, we set fBreak to true only if, at the completion of all the commands, the
@ -4330,6 +4357,23 @@ if (DEBUGGER) {
return dbgAddr; return dbgAddr;
}; };
/**
* parseAddrOptions(dbdAddr, sOptions)
*
* @this {Debugger}
* @param {DbgAddr} dbgAddr
* @param {string} sOptions
*/
Debugger.prototype.parseAddrOptions = function(dbgAddr, sOptions)
{
if (sOptions) {
var a = sOptions.match(/(['"])(.*?)\1/);
if (a) {
dbgAddr.aCmds = this.parseCommand(dbgAddr.sCmd = a[2]);
}
}
};
Debugger.aBinOpPrecedence = { Debugger.aBinOpPrecedence = {
'||': 0, // logical OR '||': 0, // logical OR
'&&': 1, // logical AND '&&': 1, // logical AND
@ -4538,7 +4582,8 @@ if (DEBUGGER) {
* *
* Returns the given string with any "{expression}" sequences replaced with the value of the expression, * Returns the given string with any "{expression}" sequences replaced with the value of the expression,
* and any "[address]" references replaced with the contents of the address. Expressions are parsed BEFORE * and any "[address]" references replaced with the contents of the address. Expressions are parsed BEFORE
* addresses. * addresses. Owing to this function's simplistic parsing, nested braces/brackets are not supported
* (define intermediate variables if needed).
* *
* @this {Debugger} * @this {Debugger}
* @param {string} sValue * @param {string} sValue
@ -4547,11 +4592,11 @@ if (DEBUGGER) {
Debugger.prototype.parseReference = function(sValue) Debugger.prototype.parseReference = function(sValue)
{ {
var a; var a;
while (a = sValue.match(/\{(.*)\}/)) { while (a = sValue.match(/\{(.*?)\}/)) {
var value = this.parseExpression(a[1]); var value = this.parseExpression(a[1]);
sValue = sValue.replace('{' + a[1] + '}', value != null? str.toHex(value) : "undefined"); sValue = sValue.replace('{' + a[1] + '}', value != null? str.toHex(value) : "undefined");
} }
while (a = sValue.match(/\[(.*)\]/)) { while (a = sValue.match(/\[(.*?)\]/)) {
var sAddr = this.parseReference(a[1]); // take care of any inner references, too var sAddr = this.parseReference(a[1]); // take care of any inner references, too
var dbgAddr = this.parseAddr(sAddr); var dbgAddr = this.parseAddr(sAddr);
sValue = sValue.replace('[' + a[1] + ']', dbgAddr? str.toHex(this.getWord(dbgAddr), dbgAddr.fData32? 8 : 4) : "undefined"); sValue = sValue.replace('[' + a[1] + ']', dbgAddr? str.toHex(this.getWord(dbgAddr), dbgAddr.fData32? 8 : 4) : "undefined");
@ -5093,13 +5138,8 @@ if (DEBUGGER) {
if (dbgAddr.off == null) return; if (dbgAddr.off == null) return;
if (sOptions) { this.parseAddrOptions(dbgAddr, sOptions);
var a = sOptions.match(/(['"])(.*?)\1/);
if (a) {
dbgAddr.sCmd = a[2];
dbgAddr.aCmds = this.parseCommand(dbgAddr.sCmd);
}
}
if (sParm == 'p') { if (sParm == 'p') {
this.addBreakpoint(this.aBreakExec, dbgAddr); this.addBreakpoint(this.aBreakExec, dbgAddr);
return; return;
@ -6151,19 +6191,27 @@ if (DEBUGGER) {
}; };
/** /**
* doRun(sAddr) * doRun(sAddr, sOptions, fQuiet)
*
* NOTE: We assume that whenever we're being called with fQuiet set to true, that we're being
* called in the context of checkBreakpoint(), and therefore the CPU is already running, and
* therefore we can skip the runCPU() call -- because if we don't, then breakpoint commands that
* include the "g" command will produce unwanted "noise".
* *
* @this {Debugger} * @this {Debugger}
* @param {string} sAddr * @param {string} sAddr
* @param {string} [sOptions]
* @param {boolean} [fQuiet]
*/ */
Debugger.prototype.doRun = function(sAddr) Debugger.prototype.doRun = function(sAddr, sOptions, fQuiet)
{ {
if (sAddr !== undefined) { if (sAddr !== undefined) {
var dbgAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE); var dbgAddr = this.parseAddr(this.parseReference(sAddr), Debugger.ADDR_CODE);
if (!dbgAddr) return; if (!dbgAddr) return;
this.parseAddrOptions(dbgAddr, sOptions);
this.setTempBreakpoint(dbgAddr); this.setTempBreakpoint(dbgAddr);
} }
if (!this.runCPU(true)) { if (!fQuiet && !this.runCPU(true)) {
this.println('cpu busy, "g" command ignored'); this.println('cpu busy, "g" command ignored');
} }
}; };
@ -6668,7 +6716,7 @@ if (DEBUGGER) {
this.doFreqs(asArgs[1]); this.doFreqs(asArgs[1]);
break; break;
case 'g': case 'g':
this.doRun(asArgs[1]); this.doRun(asArgs[1], sCmd, fQuiet);
break; break;
case 'h': case 'h':
this.doHalt(fQuiet); this.doHalt(fQuiet);