|
|
1.1 root 1: /*
2: Hatari - breakcond.c
3:
1.1.1.8 root 4: Copyright (c) 2009-2016 by Eero Tamminen
1.1 root 5:
1.1.1.5 root 6: This file is distributed under the GNU General Public License, version 2
7: or at your option any later version. Read the file gpl.txt for details.
1.1 root 8:
9: breakcond.c - code for breakpoint conditions that can check variable
10: and memory values against each other, mask them etc. before deciding
11: whether the breakpoint should be triggered. See BreakCond_Help()
12: for the syntax.
13: */
14: const char BreakCond_fileid[] = "Hatari breakcond.c : " __DATE__ " " __TIME__;
15:
16: #include <ctype.h>
17: #include <stdlib.h>
18: #include "config.h"
19: #include "main.h"
1.1.1.2 root 20: #include "file.h"
1.1 root 21: #include "m68000.h"
22: #include "memorySnapShot.h"
23: #include "dsp.h"
24: #include "stMemory.h"
25: #include "str.h"
1.1.1.8 root 26: #include "vars.h"
1.1 root 27:
28: #include "debug_priv.h"
29: #include "breakcond.h"
30: #include "debugcpu.h"
1.1.1.6 root 31: #include "debugdsp.h"
1.1.1.2 root 32: #include "debugInfo.h"
33: #include "debugui.h"
1.1 root 34: #include "evaluate.h"
1.1.1.3 root 35: #include "history.h"
1.1 root 36: #include "symbols.h"
1.1.1.2 root 37: #include "68kDisass.h"
1.1 root 38:
39:
40: /* set to 1 to enable parsing function tracing / debug output */
41: #define DEBUG 0
42:
43: /* needs to go through long long to handle x=32 */
44: #define BITMASK(x) ((Uint32)(((unsigned long long)1<<(x))-1))
45:
46: #define BC_DEFAULT_DSP_SPACE 'P'
47:
48: typedef struct {
49: bool is_indirect;
50: char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */
51: value_t valuetype; /* Hatari value variable type */
52: union {
53: Uint32 number;
54: Uint16 (*func16)(void);
55: Uint32 (*func32)(void);
56: Uint16 *reg16;
57: Uint32 *reg32;
58: } value;
59: Uint32 bits; /* CPU has 8/16/32 bit address widths */
60: Uint32 mask; /* <width mask> && <value mask> */
61: } bc_value_t;
62:
63: typedef struct {
64: bc_value_t lvalue;
65: bc_value_t rvalue;
66: char comparison;
67: bool track; /* track value changes */
68: } bc_condition_t;
69:
70: typedef struct {
1.1.1.2 root 71: char *filename; /* file where to read commands to do on hit */
72: int skip; /* how many times to hit before breaking */
73: bool once; /* remove after hit&break */
1.1.1.5 root 74: bool quiet; /* no output from setting & hitting */
1.1.1.2 root 75: bool trace; /* trace mode, don't break */
1.1.1.5 root 76: bool noinit; /* prevent debugger inits on break */
1.1.1.2 root 77: bool lock; /* tracing + show locked info */
1.1.1.7 root 78: bool deleted; /* delayed delete flag */
1.1.1.2 root 79: } bc_options_t;
80:
81: typedef struct {
1.1 root 82: char *expression;
1.1.1.2 root 83: bc_options_t options;
1.1.1.6 root 84: bc_condition_t *conditions;
1.1 root 85: int ccount; /* condition count */
86: int hits; /* how many times breakpoint hit */
87: } bc_breakpoint_t;
88:
1.1.1.7 root 89: typedef struct {
90: bc_breakpoint_t *breakpoint;
91: bc_breakpoint_t *breakpoint2delete; /* delayed delete of old alloc */
92: const char *name;
93: int count;
94: int allocated;
95: bool delayed_change;
96: const debug_reason_t reason;
97: } bc_breakpoints_t;
98:
99: static bc_breakpoints_t CpuBreakPoints = {
100: .name = "CPU",
101: .reason = REASON_CPU_BREAKPOINT
102: };
103: static bc_breakpoints_t DspBreakPoints = {
104: .name = "DSP",
105: .reason = REASON_DSP_BREAKPOINT
106: };
1.1 root 107:
108:
109: /* forward declarations */
1.1.1.8 root 110: static void BreakCond_DoDelayedActions(bc_breakpoints_t *bps);
1.1.1.7 root 111: static bool BreakCond_Remove(bc_breakpoints_t *bps, int position);
1.1 root 112: static void BreakCond_Print(bc_breakpoint_t *bp);
113:
114:
115: /**
116: * Save breakpoints as debugger input file
117: * return true for success, false for failure
118: */
119: bool BreakCond_Save(const char *filename)
120: {
121: FILE *fp;
122: int i;
123:
1.1.1.7 root 124: if (!(CpuBreakPoints.count || DspBreakPoints.count)) {
1.1.1.2 root 125: if (File_Exists(filename)) {
126: if (remove(filename)) {
127: perror("ERROR");
128: return false;
129: }
1.1 root 130: }
131: return true;
132: }
133:
134: fprintf(stderr, "Saving breakpoints to '%s'...\n", filename);
135: fp = fopen(filename, "w");
136: if (!fp) {
137: perror("ERROR");
138: return false;
139: }
140: /* save conditional breakpoints as debugger input file */
1.1.1.7 root 141: for (i = 0; i < CpuBreakPoints.count; i++) {
142: fprintf(fp, "b %s\n", CpuBreakPoints.breakpoint[i].expression);
1.1 root 143: }
1.1.1.7 root 144: for (i = 0; i < DspBreakPoints.count; i++) {
145: fprintf(fp, "db %s\n", DspBreakPoints.breakpoint[i].expression);
1.1 root 146: }
147: fclose(fp);
148: return true;
149: }
150:
151:
152: /* --------------------- debugging code ------------------- */
153:
154: #if DEBUG
155: /* see parsing code for usage examples */
156: static int _traceIndent;
157: static void _spaces(void)
158: {
159: int spaces = _traceIndent;
160: while(spaces-- > 0) {
161: putchar(' '); /* fputc(' ',stdout); */
162: }
163: }
164: #define ENTERFUNC(args) { _traceIndent += 2; _spaces(); printf args ; fflush(stdout); }
165: #define EXITFUNC(args) { _spaces(); printf args ; fflush(stdout); _traceIndent -= 2; }
166: #else
167: #define ENTERFUNC(args)
168: #define EXITFUNC(args)
169: #endif
170:
171:
172: /* ------------- breakpoint condition checking, internals ------------- */
173:
174: /**
175: * Return value from given DSP memory space/address
176: */
177: static Uint32 BreakCond_ReadDspMemory(Uint32 addr, const bc_value_t *bc_value)
178: {
179: const char *dummy;
180: return DSP_ReadMemory(addr, bc_value->dsp_space, &dummy) & BITMASK(24);
181: }
182:
183: /**
184: * Return value of given size read from given ST memory address
185: */
186: static Uint32 BreakCond_ReadSTMemory(Uint32 addr, const bc_value_t *bc_value)
187: {
188: switch (bc_value->bits) {
189: case 32:
190: return STMemory_ReadLong(addr);
191: case 16:
192: return STMemory_ReadWord(addr);
193: case 8:
194: return STMemory_ReadByte(addr);
195: default:
196: fprintf(stderr, "ERROR: unknown ST address size %d!\n", bc_value->bits);
197: abort();
198: }
199: }
200:
201:
202: /**
203: * Return Uint32 value according to given bc_value_t specification
204: */
205: static Uint32 BreakCond_GetValue(const bc_value_t *bc_value)
206: {
207: Uint32 value;
208:
209: switch (bc_value->valuetype) {
210: case VALUE_TYPE_NUMBER:
211: value = bc_value->value.number;
212: break;
213: case VALUE_TYPE_FUNCTION32:
214: value = bc_value->value.func32();
215: break;
216: case VALUE_TYPE_REG16:
217: value = *(bc_value->value.reg16);
218: break;
219: case VALUE_TYPE_VAR32:
220: case VALUE_TYPE_REG32:
221: value = *(bc_value->value.reg32);
222: break;
223: default:
224: fprintf(stderr, "ERROR: unknown condition value size/type %d!\n", bc_value->valuetype);
225: abort();
226: }
227: if (bc_value->is_indirect) {
228: if (bc_value->dsp_space) {
229: value = BreakCond_ReadDspMemory(value, bc_value);
230: } else {
231: value = BreakCond_ReadSTMemory(value, bc_value);
232: }
233: }
234: return (value & bc_value->mask);
235: }
236:
237:
238: /**
1.1.1.5 root 239: * Show & update rvalue for a tracked breakpoint condition to lvalue
240: */
241: static void BreakCond_UpdateTracked(bc_condition_t *condition, Uint32 value)
242: {
243: Uint32 addr;
244:
245: /* next monitor changes to this new value */
246: condition->rvalue.value.number = value;
247:
248: if (condition->lvalue.is_indirect &&
249: condition->lvalue.valuetype == VALUE_TYPE_NUMBER) {
250: /* simple memory address */
251: addr = condition->lvalue.value.number;
252: fprintf(stderr, " $%x = $%x\n", addr, value);
253: } else {
254: /* register tms. */
255: fprintf(stderr, " $%x\n", value);
256: }
257: }
258:
259:
260: /**
1.1 root 261: * Return true if all of the given breakpoint's conditions match
262: */
1.1.1.5 root 263: static bool BreakCond_MatchConditions(bc_condition_t *condition, int count)
1.1 root 264: {
265: Uint32 lvalue, rvalue;
266: bool hit = false;
267: int i;
268:
269: for (i = 0; i < count; condition++, i++) {
270:
271: lvalue = BreakCond_GetValue(&(condition->lvalue));
272: rvalue = BreakCond_GetValue(&(condition->rvalue));
273:
274: switch (condition->comparison) {
275: case '<':
276: hit = (lvalue < rvalue);
277: break;
278: case '>':
279: hit = (lvalue > rvalue);
280: break;
281: case '=':
282: hit = (lvalue == rvalue);
283: break;
284: case '!':
285: hit = (lvalue != rvalue);
286: break;
287: default:
288: fprintf(stderr, "ERROR: Unknown breakpoint value comparison operator '%c'!\n",
289: condition->comparison);
290: abort();
291: }
1.1.1.7 root 292: if (likely(!hit)) {
1.1 root 293: return false;
294: }
1.1.1.6 root 295: if (condition->track) {
296: BreakCond_UpdateTracked(condition, lvalue);
297: }
1.1 root 298: }
299: /* all conditions matched */
300: return true;
301: }
302:
303:
304: /**
1.1.1.8 root 305: * Check and show which breakpoints' conditions matched
306: * @return true if (non-tracing) breakpoint was hit,
307: * or false if none matched
1.1 root 308: */
1.1.1.8 root 309: static bool BreakCond_MatchBreakPoints(bc_breakpoints_t *bps)
1.1 root 310: {
1.1.1.7 root 311: bc_breakpoint_t *bp;
312: bool changes = false;
1.1.1.8 root 313: bool hit = false;
314: int i;
1.1.1.5 root 315:
1.1.1.7 root 316: /* array should not be changed while it's being traversed */
317: assert(likely(!bps->delayed_change));
318: bps->delayed_change = true;
1.1.1.2 root 319:
1.1.1.7 root 320: bp = bps->breakpoint;
321: for (i = 0; i < bps->count; bp++, i++) {
1.1.1.2 root 322:
1.1.1.7 root 323: if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) {
1.1 root 324: bp->hits++;
1.1.1.5 root 325: if (bp->options.skip) {
326: if (bp->hits % bp->options.skip) {
327: /* check next */
328: continue;
329: }
330: }
331: if (!bp->options.quiet) {
332: fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n",
1.1.1.7 root 333: i+1, bps->name, bp->hits);
1.1.1.5 root 334: BreakCond_Print(bp);
1.1 root 335: }
1.1.1.7 root 336: History_Mark(bps->reason);
1.1.1.3 root 337:
1.1.1.5 root 338: if (bp->options.lock || bp->options.filename) {
339: bool reinit = !bp->options.noinit;
1.1.1.3 root 340:
1.1.1.5 root 341: if (reinit) {
342: DebugCpu_InitSession();
343: DebugDsp_InitSession();
344: }
345:
346: if (bp->options.lock) {
347: DebugInfo_ShowSessionInfo();
348: }
349: if (bp->options.filename) {
350: DebugUI_ParseFile(bp->options.filename, reinit);
1.1.1.7 root 351: changes = true;
1.1.1.5 root 352: }
1.1.1.3 root 353: }
354: if (bp->options.once) {
1.1.1.7 root 355: BreakCond_Remove(bps, i+1);
356: changes = true;
1.1.1.2 root 357: }
1.1.1.5 root 358: if (!bp->options.trace) {
1.1.1.7 root 359: /* index for current hit, they start from 1 */
1.1.1.8 root 360: hit = true;
1.1.1.5 root 361: }
362: /* continue checking breakpoints to make sure all relevant actions get performed */
1.1 root 363: }
364: }
1.1.1.7 root 365: bps->delayed_change = false;
366: if (unlikely(changes)) {
1.1.1.8 root 367: BreakCond_DoDelayedActions(bps);
1.1.1.7 root 368: }
1.1.1.8 root 369: return hit;
1.1 root 370: }
371:
372: /* ------------- breakpoint condition checking, public API ------------- */
373:
374: /**
1.1.1.8 root 375: * Return true if there were CPU breakpoint hits, false otherwise.
1.1 root 376: */
1.1.1.8 root 377: bool BreakCond_MatchCpu(void)
1.1 root 378: {
1.1.1.7 root 379: return BreakCond_MatchBreakPoints(&CpuBreakPoints);
1.1 root 380: }
381:
382: /**
1.1.1.8 root 383: * Return true if there were DSP breakpoint hits, false otherwise.
1.1 root 384: */
1.1.1.8 root 385: bool BreakCond_MatchDsp(void)
1.1 root 386: {
1.1.1.7 root 387: return BreakCond_MatchBreakPoints(&DspBreakPoints);
1.1 root 388: }
389:
390: /**
1.1.1.7 root 391: * Return number of CPU condition breakpoints
1.1 root 392: */
1.1.1.7 root 393: int BreakCond_CpuBreakPointCount(void)
1.1 root 394: {
1.1.1.7 root 395: return CpuBreakPoints.count;
396: }
397:
398: /**
399: * Return number of DSP condition breakpoints
400: */
401: int BreakCond_DspBreakPointCount(void)
402: {
403: return DspBreakPoints.count;
1.1 root 404: }
405:
406:
407: /* -------------- breakpoint condition parsing, internals ------------- */
408:
409: /* struct for passing around breakpoint conditions parsing state */
410: typedef struct {
411: int arg; /* current arg */
412: int argc; /* arg count */
413: const char **argv; /* arg pointer array (+ strings) */
414: const char *error; /* error from parsing args */
415: } parser_state_t;
416:
417:
418: /**
419: * If given string is a Hatari variable name, set bc_value
420: * fields accordingly and return true, otherwise return false.
421: */
422: static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value)
423: {
1.1.1.7 root 424: const var_addr_t *hvar;
1.1 root 425:
426: ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name));
1.1.1.8 root 427: hvar = Vars_ParseVariable(name);
428: if (hvar) {
429: bc_value->value.reg32 = hvar->addr;
430: bc_value->valuetype = hvar->vtype;
431: bc_value->bits = hvar->bits;
432: assert(bc_value->bits == 32 || bc_value->valuetype != VALUE_TYPE_VAR32);
433: EXITFUNC(("-> true\n"));
434: return true;
435: }
1.1 root 436: EXITFUNC(("-> false\n"));
437: return false;
438: }
439:
440:
441: /**
442: * If given string matches a suitable symbol, set bc_value
443: * fields accordingly and return true, otherwise return false.
444: */
445: static bool BreakCond_ParseSymbol(const char *name, bc_value_t *bc_value)
446: {
447: symtype_t symtype;
448: Uint32 addr;
449:
450: ENTERFUNC(("BreakCond_ParseSymbol('%s')\n", name));
451: if (bc_value->is_indirect) {
452: /* indirect use of address makes sense only for data */
453: symtype = SYMTYPE_DATA|SYMTYPE_BSS;
454: } else {
455: /* direct value can be compared for anything */
456: symtype = SYMTYPE_ALL;
457: }
458:
459: if (bc_value->dsp_space) {
460: if (!Symbols_GetDspAddress(symtype, name, &addr)) {
461: EXITFUNC(("-> false (DSP)\n"));
462: return false;
463: }
464: /* all DSP memory values are 24-bits */
465: bc_value->bits = 24;
466: bc_value->value.number = addr;
467: bc_value->valuetype = VALUE_TYPE_NUMBER;
468: EXITFUNC(("-> true (DSP)\n"));
469: return true;
470: }
471:
472: if (!Symbols_GetCpuAddress(symtype, name, &addr)) {
473: EXITFUNC(("-> false (CPU)\n"));
474: return false;
475: }
476: if (addr & 1) {
477: /* only bytes can be at odd addresses */
478: bc_value->bits = 8;
479: } else {
480: bc_value->bits = 32;
481: }
482: bc_value->value.number = addr;
483: bc_value->valuetype = VALUE_TYPE_NUMBER;
484: EXITFUNC(("-> true (CPU)\n"));
485: return true;
486: }
487:
488:
489: /**
490: * Helper function to get CPU PC register value with static inline as Uint32
491: */
492: static Uint32 GetCpuPC(void)
493: {
494: return M68000_GetPC();
495: }
496: /**
497: * Helper function to get CPU SR register value with static inline as Uint32
498: */
499: static Uint32 GetCpuSR(void)
500: {
501: return M68000_GetSR();
502: }
503:
504: /**
505: * If given string is register name (for DSP or CPU), set bc_value
506: * fields accordingly and return true, otherwise return false.
507: */
508: static bool BreakCond_ParseRegister(const char *regname, bc_value_t *bc_value)
509: {
510: int regsize;
511: ENTERFUNC(("BreakCond_ParseRegister('%s')\n", regname));
512: if (bc_value->dsp_space) {
513: regsize = DSP_GetRegisterAddress(regname,
514: &(bc_value->value.reg32),
515: &(bc_value->mask));
516: if (regsize) {
1.1.1.6 root 517: if (bc_value->is_indirect
518: && toupper((unsigned char)regname[0]) != 'R') {
1.1 root 519: fprintf(stderr, "ERROR: only R0-R7 DSP registers can be used for indirect addressing!\n");
520: EXITFUNC(("-> false (DSP)\n"));
521: return false;
522: }
523: /* all DSP memory values are 24-bits */
524: bc_value->bits = 24;
525: bc_value->valuetype = regsize;
526: EXITFUNC(("-> true (DSP)\n"));
527: return true;
528: }
529: EXITFUNC(("-> false (DSP)\n"));
530: return false;
531: }
532: regsize = DebugCpu_GetRegisterAddress(regname, &(bc_value->value.reg32));
533: if (regsize) {
534: bc_value->bits = regsize;
535: /* valuetypes for registers are 16 & 32 */
536: bc_value->valuetype = regsize;
537: EXITFUNC(("-> true (CPU)\n"));
538: return true;
539: }
540: /* Exact UAE core 32-bit PC & 16-bit SR register values
1.1.1.4 root 541: * can be gotten only through UAE accessors, not directly
1.1 root 542: */
543: if (strcasecmp(regname, "PC") == 0) {
544: bc_value->bits = 32;
545: bc_value->value.func32 = GetCpuPC;
546: bc_value->valuetype = VALUE_TYPE_FUNCTION32;
547: EXITFUNC(("-> true (CPU)\n"));
548: return true;
549: }
550: if (strcasecmp(regname, "SR") == 0) {
551: bc_value->bits = 16;
552: bc_value->value.func32 = GetCpuSR;
553: bc_value->valuetype = VALUE_TYPE_FUNCTION32;
554: EXITFUNC(("-> true (CPU)\n"));
555: return true;
556: }
557: EXITFUNC(("-> false (CPU)\n"));
558: return false;
559: }
560:
561: /**
562: * If given address is valid (for DSP or CPU), return true.
563: */
564: static bool BreakCond_CheckAddress(bc_value_t *bc_value)
565: {
1.1.1.7 root 566: Uint32 addr = bc_value->value.number;
567: int size = bc_value->bits >> 8;
1.1 root 568:
569: ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr));
570: if (bc_value->dsp_space) {
1.1.1.7 root 571: if (addr+size > 0xFFFF) {
1.1 root 572: EXITFUNC(("-> false (DSP)\n"));
573: return false;
574: }
575: EXITFUNC(("-> true (DSP)\n"));
576: return true;
577: }
1.1.1.7 root 578: if (!STMemory_CheckAreaType(addr, size, ABFLAG_RAM | ABFLAG_ROM | ABFLAG_IO)) {
1.1 root 579: EXITFUNC(("-> false (CPU)\n"));
580: return false;
581: }
582: EXITFUNC(("-> true (CPU)\n"));
583: return true;
584: }
585:
586:
587: /**
588: * Check for and parse a condition value address space/width modifier.
589: * Modify pstate according to parsing (arg index and error string).
590: * Return false for error and true for no or successfully parsed modifier.
591: */
592: static bool BreakCond_ParseAddressModifier(parser_state_t *pstate, bc_value_t *bc_value)
593: {
594: char mode;
595:
596: ENTERFUNC(("BreakCond_ParseAddressModifier()\n"));
597: if (pstate->arg+2 > pstate->argc ||
598: strcmp(pstate->argv[pstate->arg], ".") != 0) {
599: if (bc_value->dsp_space && bc_value->is_indirect) {
600: pstate->error = "DSP memory addresses need to specify address space";
601: EXITFUNC(("arg:%d -> false\n", pstate->arg));
602: return false;
603: }
604: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
605: return true;
606: }
607: if (!bc_value->is_indirect) {
1.1.1.9 ! root 608: pstate->error = "space/width modifier can be used only with an (address) expression\n"
! 609: "(note that you can use a mask instead of width, for example: 'd0 & 0xff')";
1.1 root 610: EXITFUNC(("arg:%d -> false\n", pstate->arg));
611: return false;
612: }
613: pstate->arg++;
614: if (bc_value->dsp_space) {
615: switch (pstate->argv[pstate->arg][0]) {
616: case 'p':
617: case 'x':
618: case 'y':
1.1.1.6 root 619: mode = toupper((unsigned char)pstate->argv[pstate->arg][0]);
1.1 root 620: break;
621: default:
622: pstate->error = "invalid address space modifier";
623: EXITFUNC(("arg:%d -> false\n", pstate->arg));
624: return false;
625: }
626: } else {
627: switch (pstate->argv[pstate->arg][0]) {
628: case 'l':
629: mode = 32;
630: break;
631: case 'w':
632: mode = 16;
633: break;
634: case 'b':
635: mode = 8;
636: break;
637: default:
638: pstate->error = "invalid address width modifier";
639: EXITFUNC(("arg:%d -> false\n", pstate->arg));
640: return false;
641: }
642: }
643: if (pstate->argv[pstate->arg][1]) {
644: pstate->error = "invalid address space/width modifier";
645: EXITFUNC(("arg:%d -> false\n", pstate->arg));
646: return false;
647: }
648: if (bc_value->dsp_space) {
649: bc_value->dsp_space = mode;
650: EXITFUNC(("arg:%d -> space:%c, true\n", pstate->arg, mode));
651: } else {
652: bc_value->bits = mode;
653: EXITFUNC(("arg:%d -> width:%d, true\n", pstate->arg, mode));
654: }
655: pstate->arg++;
656: return true;
657: }
658:
659:
660: /**
661: * Check for and parse a condition value mask.
662: * Modify pstate according to parsing (arg index and error string).
663: * Return false for error and true for no or successfully parsed modifier.
664: */
665: static bool BreakCond_ParseMaskModifier(parser_state_t *pstate, bc_value_t *bc_value)
666: {
667: ENTERFUNC(("BreakCond_ParseMaskModifier()\n"));
668: if (pstate->arg+2 > pstate->argc ||
669: strcmp(pstate->argv[pstate->arg], "&") != 0) {
670: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
671: return true;
672: }
673: if (bc_value->valuetype == VALUE_TYPE_NUMBER &&
674: !bc_value->is_indirect) {
675: fprintf(stderr, "WARNING: plain numbers shouldn't need masks.\n");
676: }
677: pstate->arg++;
678: if (!Eval_Number(pstate->argv[pstate->arg], &(bc_value->mask))) {
679: pstate->error = "invalid dec/hex/bin value";
680: EXITFUNC(("arg:%d -> false\n", pstate->arg));
681: return false;
682: }
683: if (bc_value->mask == 0 ||
684: (bc_value->valuetype == VALUE_TYPE_NUMBER && !bc_value->is_indirect &&
685: bc_value->value.number && !(bc_value->value.number & bc_value->mask))) {
686: pstate->error = "mask zeroes value";
687: EXITFUNC(("arg:%d -> false\n", pstate->arg));
688: return false;
689: }
690: EXITFUNC(("arg:%d -> true (%x)\n", pstate->arg, bc_value->mask));
691: pstate->arg++;
692: return true;
693: }
694:
695:
696: /**
697: * Parse a breakpoint condition value.
698: * Modify pstate according to parsing (arg index and error string).
699: * Return true for success and false for error.
700: */
701: static bool BreakCond_ParseValue(parser_state_t *pstate, bc_value_t *bc_value)
702: {
703: const char *str;
704: int skip = 1;
705:
706: ENTERFUNC(("BreakCond_Value()\n"));
707: if (pstate->arg >= pstate->argc) {
708: pstate->error = "value missing";
709: EXITFUNC(("arg:%d -> false\n", pstate->arg));
710: return false;
711: }
712: /* parse indirection */
713: if (pstate->arg+3 <= pstate->argc) {
714: if (strcmp(pstate->argv[pstate->arg+0], "(") == 0 &&
715: strcmp(pstate->argv[pstate->arg+2], ")") == 0) {
716: bc_value->is_indirect = true;
717: pstate->arg++;
718: skip = 2;
719: }
720: }
721:
722: str = pstate->argv[pstate->arg];
1.1.1.6 root 723: if (isalpha((unsigned char)*str) || *str == '_') {
1.1 root 724: /* parse direct or indirect variable/register/symbol name */
725: if (bc_value->is_indirect) {
726: /* a valid register or data symbol name? */
727: if (!BreakCond_ParseRegister(str, bc_value) &&
728: !BreakCond_ParseSymbol(str, bc_value)) {
729: pstate->error = "invalid register/symbol name for indirection";
730: EXITFUNC(("arg:%d -> false\n", pstate->arg));
731: return false;
732: }
733: } else {
734: /* a valid Hatari variable or register name?
735: * variables cannot be used for ST memory indirection.
736: */
737: if (!BreakCond_ParseVariable(str, bc_value) &&
738: !BreakCond_ParseRegister(str, bc_value) &&
739: !BreakCond_ParseSymbol(str, bc_value)) {
740: pstate->error = "invalid variable/register/symbol name";
741: EXITFUNC(("arg:%d -> false\n", pstate->arg));
742: return false;
743: }
744: }
745: } else {
746: /* a number */
747: if (!Eval_Number(str, &(bc_value->value.number))) {
748: pstate->error = "invalid dec/hex/bin value";
749: EXITFUNC(("arg:%d -> false\n", pstate->arg));
750: return false;
751: }
752: }
753: /* memory address (indirect value) -> OK as address? */
754: if (bc_value->is_indirect &&
755: bc_value->valuetype == VALUE_TYPE_NUMBER &&
756: !BreakCond_CheckAddress(bc_value)) {
757: pstate->error = "invalid address";
758: EXITFUNC(("arg:%d -> false\n", pstate->arg));
759: return false;
760: }
761: pstate->arg += skip;
762:
763: /* parse modifiers */
764: if (!BreakCond_ParseAddressModifier(pstate, bc_value)) {
765: EXITFUNC(("arg:%d -> false\n", pstate->arg));
766: return false;
767: }
768: if (!BreakCond_ParseMaskModifier(pstate, bc_value)) {
769: EXITFUNC(("arg:%d -> false\n", pstate->arg));
770: return false;
771: }
772: EXITFUNC(("arg:%d -> true (%s value)\n", pstate->arg,
773: (bc_value->is_indirect ? "indirect" : "direct")));
774: return true;
775: }
776:
777:
778: /**
779: * Parse a breakpoint comparison character.
780: * Modify pstate according to parsing (arg index and error string).
781: * Return the character or nil for an error.
782: */
783: static char BreakCond_ParseComparison(parser_state_t *pstate)
784: {
785: const char *comparison;
786:
787: ENTERFUNC(("BreakCond_ParseComparison(), arg:%d\n", pstate->arg));
788: if (pstate->arg >= pstate->argc) {
789: pstate->error = "breakpoint comparison missing";
790: EXITFUNC(("-> false\n"));
791: return false;
792: }
793: comparison = pstate->argv[pstate->arg];
794: switch (comparison[0]) {
795: case '<':
796: case '>':
797: case '=':
798: case '!':
799: break;
800: default:
801: pstate->error = "invalid comparison character";
802: EXITFUNC(("-> false\n"));
803: return false;
804: }
805: if (comparison[1]) {
806: pstate->error = "trailing comparison character(s)";
807: EXITFUNC(("-> false\n"));
808: return false;
809: }
810:
811: pstate->arg++;
812: if (pstate->arg >= pstate->argc) {
813: pstate->error = "right side missing";
814: EXITFUNC(("-> false\n"));
815: return false;
816: }
817: EXITFUNC(("-> '%c'\n", *comparison));
818: return *comparison;
819: }
820:
821:
822: /**
823: * If no value, use the other value, if that also missing, use default
824: */
825: static void BreakCond_InheritDefault(Uint32 *value1, Uint32 value2, Uint32 defvalue)
826: {
827: if (!*value1) {
828: if (value2) {
829: *value1 = value2;
830: } else {
831: *value1 = defvalue;
832: }
833: }
834: }
835:
836: /**
837: * Check & ensure that the masks and address sizes are sane
838: * and allow comparison with the other side.
839: * If yes, return true, otherwise false.
840: */
841: static bool BreakCond_CrossCheckValues(parser_state_t *pstate,
842: bc_value_t *bc_value1,
843: bc_value_t *bc_value2)
844: {
845: Uint32 mask1, mask2, defbits;
846: ENTERFUNC(("BreakCond_CrossCheckValues()\n"));
847:
848: /* make sure there're valid bit widths and that masks have some value */
849: if (bc_value1->dsp_space) {
850: defbits = 24;
851: } else {
852: defbits = 32;
853: }
854: BreakCond_InheritDefault(&(bc_value1->bits), bc_value2->bits, defbits);
855: BreakCond_InheritDefault(&(bc_value2->bits), bc_value1->bits, defbits);
856: BreakCond_InheritDefault(&(bc_value1->mask), bc_value2->mask, BITMASK(bc_value1->bits));
857: BreakCond_InheritDefault(&(bc_value2->mask), bc_value1->mask, BITMASK(bc_value2->bits));
858:
859: /* check first value mask & bit width */
860: mask1 = BITMASK(bc_value1->bits) & bc_value1->mask;
861:
862: if (mask1 != bc_value1->mask) {
863: fprintf(stderr, "WARNING: mask 0x%x doesn't fit into %d address/register bits.\n",
864: bc_value1->mask, bc_value1->bits);
865: }
866: if (!bc_value1->dsp_space &&
867: bc_value1->is_indirect &&
868: (bc_value1->value.number & 1) && bc_value1->bits > 8) {
869: fprintf(stderr, "WARNING: odd CPU address 0x%x given without using byte (.b) width.\n",
870: bc_value1->value.number);
871: }
872:
873: /* cross-check both values masks */
874: mask2 = BITMASK(bc_value2->bits) & bc_value2->mask;
875:
876: if ((mask1 & mask2) == 0) {
877: pstate->error = "values masks cancel each other";
878: EXITFUNC(("-> false\n"));
879: return false;
880: }
881: if (bc_value2->is_indirect ||
882: bc_value2->value.number == 0 ||
883: bc_value2->valuetype != VALUE_TYPE_NUMBER) {
884: EXITFUNC(("-> true (no problematic direct types)\n"));
885: return true;
886: }
887: if ((bc_value2->value.number & mask1) != bc_value2->value.number) {
888: pstate->error = "number doesn't fit the other side address width&mask";
889: EXITFUNC(("-> false\n"));
890: return false;
891: }
892: EXITFUNC(("-> true\n"));
893: return true;
894: }
895:
896:
897: /**
898: * Parse given breakpoint conditions and append them to breakpoints.
899: * Modify pstate according to parsing (arg index and error string).
900: * Return number of added conditions or zero for failure.
901: */
902: static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp,
1.1.1.6 root 903: bc_breakpoint_t *bp, int ccount)
1.1 root 904: {
905: bc_condition_t condition;
906:
907: ENTERFUNC(("BreakCond_ParseCondition(...)\n"));
908:
909: /* setup condition */
910: memset(&condition, 0, sizeof(bc_condition_t));
911: if (bForDsp) {
912: /* used also for checking whether value is for DSP */
913: condition.lvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
914: condition.rvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
915: }
916:
917: /* parse condition */
918: if (!BreakCond_ParseValue(pstate, &(condition.lvalue))) {
919: EXITFUNC(("-> 0\n"));
920: return 0;
921: }
922: condition.comparison = BreakCond_ParseComparison(pstate);
923: if (!condition.comparison) {
924: EXITFUNC(("-> 0\n"));
925: return 0;
926: }
927: if (!BreakCond_ParseValue(pstate, &(condition.rvalue))) {
928: EXITFUNC(("-> 0\n"));
929: return 0;
930: }
931: if (!(BreakCond_CrossCheckValues(pstate, &(condition.lvalue), &(condition.rvalue)) &&
932: BreakCond_CrossCheckValues(pstate, &(condition.rvalue), &(condition.lvalue)))) {
933: EXITFUNC(("-> 0\n"));
934: return 0;
935: }
1.1.1.6 root 936: /* copy new condition */
937: ccount += 1;
938: bp->conditions = realloc(bp->conditions, sizeof(bc_condition_t)*(ccount));
939: if (!bp->conditions) {
940: pstate->error = "failed to allocate space for breakpoint condition";
941: EXITFUNC(("-> 0\n"));
942: return 0;
943: }
944: bp->conditions[ccount-1] = condition;
1.1 root 945:
946: /* continue with next condition? */
947: if (pstate->arg == pstate->argc) {
1.1.1.6 root 948: EXITFUNC(("-> %d conditions (all args parsed)\n", ccount));
1.1 root 949: return ccount;
950: }
951: if (strcmp(pstate->argv[pstate->arg], "&&") != 0) {
952: pstate->error = "trailing content for breakpoint condition";
953: EXITFUNC(("-> 0\n"));
954: return 0;
955: }
956: pstate->arg++;
957:
958: /* recurse conditions parsing */
1.1.1.6 root 959: ccount = BreakCond_ParseCondition(pstate, bForDsp, bp, ccount);
1.1 root 960: if (!ccount) {
961: EXITFUNC(("-> 0\n"));
962: return 0;
963: }
1.1.1.6 root 964: EXITFUNC(("-> %d conditions (recursed)\n", ccount));
1.1 root 965: return ccount;
966: }
967:
968:
969: /**
970: * Tokenize given breakpoint expression to given parser struct.
971: * Return normalized expression string that corresponds to tokenization
972: * or NULL on error. On error, pstate->error contains the error message
973: * and pstate->arg index to invalid character (instead of to token like
974: * after parsing).
975: */
976: static char *BreakCond_TokenizeExpression(const char *expression,
977: parser_state_t *pstate)
978: {
979: char separator[] = {
980: '=', '!', '<', '>', /* comparison operators */
981: '(', ')', '.', '&', /* other separators */
982: '\0' /* terminator */
983: };
984: bool is_separated, has_comparison;
985: char sep, *dst, *normalized;
986: const char *src;
987: int i, tokens;
988:
989: memset(pstate, 0, sizeof(parser_state_t));
990:
991: /* _minimum_ safe size for normalized expression is 2x+1 */
992: normalized = malloc(2*strlen(expression)+1);
993: if (!normalized) {
994: pstate->error = "alloc failed";
995: return NULL;
996: }
997:
998: /* check characters & normalize string */
999: dst = normalized;
1000: is_separated = false;
1001: has_comparison = false;
1002: for (src = expression; *src; src++) {
1003: /* discard white space in source */
1.1.1.6 root 1004: if (isspace((unsigned char)*src)) {
1.1 root 1005: continue;
1006: }
1007: /* separate tokens with single space in destination */
1008: for (i = 0; (sep = separator[i]); i++) {
1009: if (*src == sep) {
1010: if (dst > normalized) {
1011: /* don't separate boolean AND '&&' */
1012: if (*src == '&' && *(src-1) == '&') {
1013: dst--;
1014: } else {
1015: if (!is_separated) {
1016: *dst++ = ' ';
1017: }
1018: }
1019: }
1020: *dst++ = *src;
1021: *dst++ = ' ';
1022: is_separated = true;
1023: if (i < 4) {
1024: has_comparison = true;
1025: }
1026: break;
1027: }
1028: }
1029: /* validate & copy other characters */
1030: if (!sep) {
1031: /* variable/register/symbol or number prefix? */
1.1.1.6 root 1032: if (!(isalnum((unsigned char)*src) || *src == '_' ||
1.1 root 1033: *src == '$' || *src == '#' || *src == '%')) {
1034: pstate->error = "invalid character";
1035: pstate->arg = src-expression;
1036: free(normalized);
1037: return NULL;
1038: }
1039: *dst++ = *src;
1040: is_separated = false;
1041: }
1042: }
1043: if (is_separated) {
1044: dst--; /* no trailing space */
1045: }
1046: *dst = '\0';
1047:
1048: if (!has_comparison) {
1049: pstate->error = "condition comparison missing";
1050: pstate->arg = strlen(expression)/2;
1051: free(normalized);
1052: return NULL;
1053: }
1054:
1055: /* allocate exact space for tokenized string array + strings */
1056: tokens = 1;
1057: for (dst = normalized; *dst; dst++) {
1058: if (*dst == ' ') {
1059: tokens++;
1060: }
1061: }
1062: pstate->argv = malloc(tokens*sizeof(char*)+strlen(normalized)+1);
1063: if (!pstate->argv) {
1064: pstate->error = "alloc failed";
1065: free(normalized);
1066: return NULL;
1067: }
1068: /* and copy/tokenize... */
1069: dst = (char*)(pstate->argv) + tokens*sizeof(char*);
1070: strcpy(dst, normalized);
1071: pstate->argv[0] = strtok(dst, " ");
1072: for (i = 1; (dst = strtok(NULL, " ")); i++) {
1073: pstate->argv[i] = dst;
1074: }
1075: assert(i == tokens);
1076: pstate->argc = tokens;
1077: #if DEBUG
1078: fprintf(stderr, "args->");
1079: for (i = 0; i < tokens; i++) {
1080: fprintf(stderr, " %d: %s,", i, pstate->argv[i]);
1081: }
1082: fprintf(stderr, "\n");
1083: #endif
1084: return normalized;
1085: }
1086:
1087:
1088: /**
1.1.1.7 root 1089: * Select corrent breakpoints struct and provide name for it.
1.1.1.6 root 1090: * Make sure there's always space for at least one additional breakpoint.
1.1.1.7 root 1091: * Return pointer to the breakpoints struct
1.1 root 1092: */
1.1.1.7 root 1093: static bc_breakpoints_t* BreakCond_GetListInfo(bool bForDsp)
1.1 root 1094: {
1.1.1.7 root 1095: bc_breakpoints_t *bps;
1.1 root 1096: if (bForDsp) {
1.1.1.7 root 1097: bps = &DspBreakPoints;
1.1 root 1098: } else {
1.1.1.7 root 1099: bps = &CpuBreakPoints;
1.1 root 1100: }
1.1.1.6 root 1101: /* allocate (more) space for breakpoints when needed */
1.1.1.7 root 1102: if (bps->count + 1 >= bps->allocated) {
1103: if (!bps->allocated) {
1.1.1.6 root 1104: /* initial count of available breakpoints */
1.1.1.7 root 1105: bps->allocated = 16;
1.1.1.6 root 1106: } else {
1.1.1.7 root 1107: bps->allocated *= 2;
1.1.1.6 root 1108: }
1.1.1.7 root 1109: if (bps->delayed_change) {
1110: if(bps->breakpoint2delete) {
1111: /* getting second re-alloc within same breakpoint handler is really
1112: * unlikely, this would require adding dozens of new breakpoints.
1113: */
1114: fprintf(stderr, "ERROR: too many new breakpoints added within single breakpoint hit!\n");
1115: abort();
1116: }
1117: bps->breakpoint2delete = bps->breakpoint;
1118: bps->breakpoint = malloc(bps->allocated * sizeof(bc_breakpoint_t));
1.1.1.6 root 1119: } else {
1.1.1.7 root 1120: bps->breakpoint = realloc(bps->breakpoint, bps->allocated * sizeof(bc_breakpoint_t));
1.1.1.6 root 1121: }
1.1.1.7 root 1122: assert(bps->breakpoint);
1.1.1.6 root 1123: }
1.1.1.7 root 1124: return bps;
1.1 root 1125: }
1126:
1127:
1128: /**
1129: * Check whether any of the breakpoint conditions is such that it's
1130: * intended for tracking given value changes (inequality comparison
1131: * on identical values) or for retrieving the current value to break
1132: * on next value change (other comparisons on identical values).
1133: *
1134: * On former case, mark it for tracking, on other cases, just
1135: * retrieve the value.
1136: */
1137: static void BreakCond_CheckTracking(bc_breakpoint_t *bp)
1138: {
1139: bc_condition_t *condition;
1140: bool track = false;
1141: Uint32 value;
1142: int i;
1143:
1144: condition = bp->conditions;
1145: for (i = 0; i < bp->ccount; condition++, i++) {
1146:
1147: if (memcmp(&(condition->lvalue), &(condition->rvalue), sizeof(bc_value_t)) == 0) {
1148: /* set current value to right side */
1149: value = BreakCond_GetValue(&(condition->rvalue));
1150: condition->rvalue.value.number = value;
1151: condition->rvalue.valuetype = VALUE_TYPE_NUMBER;
1152: condition->rvalue.is_indirect = false;
1.1.1.6 root 1153: /* track those changes */
1154: if (condition->comparison != '=') {
1.1 root 1155: condition->track = true;
1156: track = true;
1157: } else {
1158: fprintf(stderr, "\t%d. condition: %c $%x\n",
1159: i+1, condition->comparison, value);
1160: }
1161: }
1162: }
1163: if (track) {
1164: fprintf(stderr, "-> Track value changes, show value(s) when matched.\n");
1165: }
1166: }
1167:
1168:
1169: /**
1170: * Parse given breakpoint expression and store it.
1171: * Return true for success and false for failure.
1172: */
1.1.1.2 root 1173: static bool BreakCond_Parse(const char *expression, bc_options_t *options, bool bForDsp)
1.1 root 1174: {
1175: parser_state_t pstate;
1.1.1.7 root 1176: bc_breakpoints_t *bps;
1.1 root 1177: bc_breakpoint_t *bp;
1178: char *normalized;
1179: int ccount;
1180:
1.1.1.7 root 1181: bps = BreakCond_GetListInfo(bForDsp);
1.1.1.6 root 1182:
1.1.1.7 root 1183: bp = bps->breakpoint + bps->count;
1.1 root 1184: memset(bp, 0, sizeof(bc_breakpoint_t));
1185:
1186: normalized = BreakCond_TokenizeExpression(expression, &pstate);
1187: if (normalized) {
1188: bp->expression = normalized;
1.1.1.6 root 1189: ccount = BreakCond_ParseCondition(&pstate, bForDsp, bp, 0);
1190: /* fail? */
1191: if (!ccount) {
1192: bp->expression = NULL;
1193: if (bp->conditions) {
1194: /* free what was allocated by ParseCondition */
1195: free(bp->conditions);
1196: bp->conditions = NULL;
1197: }
1198: }
1.1 root 1199: bp->ccount = ccount;
1200: } else {
1201: ccount = 0;
1202: }
1203: if (pstate.argv) {
1204: free(pstate.argv);
1205: }
1206: if (ccount > 0) {
1.1.1.7 root 1207: bps->count++;
1.1.1.5 root 1208: if (!options->quiet) {
1209: fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n",
1.1.1.7 root 1210: bps->name, bps->count, ccount, bp->expression);
1.1.1.5 root 1211: if (options->skip) {
1212: fprintf(stderr, "-> Break only on every %d hit.\n", options->skip);
1213: }
1214: if (options->once) {
1215: fprintf(stderr, "-> Once, delete after breaking.\n");
1216: }
1217: if (options->trace) {
1218: fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n");
1219: if (options->lock) {
1220: fprintf(stderr, "-> Show also info selected with lock command.\n");
1221: }
1222: if (options->noinit) {
1223: fprintf(stderr, "-> Skip debugger inits on hit.\n");
1224: }
1225: }
1226: if (options->filename) {
1227: fprintf(stderr, "-> Execute debugger commands from '%s' file on hit.\n", options->filename);
1.1.1.2 root 1228: }
1229: }
1.1.1.5 root 1230: BreakCond_CheckTracking(bp);
1231:
1232: bp->options.quiet = options->quiet;
1233: bp->options.skip = options->skip;
1234: bp->options.once = options->once;
1235: bp->options.trace = options->trace;
1236: bp->options.lock = options->lock;
1237: bp->options.noinit = options->noinit;
1.1.1.2 root 1238: if (options->filename) {
1239: bp->options.filename = strdup(options->filename);
1.1 root 1240: }
1241: } else {
1242: if (normalized) {
1243: int offset, i = 0;
1244: char *s = normalized;
1245: while (*s && i < pstate.arg) {
1246: if (*s++ == ' ') {
1247: i++;
1248: }
1249: }
1250: offset = s - normalized;
1251: /* show tokenized string and point out
1252: * the token where the error was encountered
1253: */
1254: fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n",
1255: normalized, offset+2, '^', pstate.error);
1256: free(normalized);
1257: } else {
1258: /* show original string and point out the character
1259: * where the error was encountered
1260: */
1261: fprintf(stderr, "ERROR in parsed string:\n'%s'\n%*c-%s\n",
1262: expression, pstate.arg+2, '^', pstate.error);
1263: }
1264: }
1265: return (ccount > 0);
1266: }
1267:
1268:
1269: /**
1270: * print single breakpoint
1271: */
1272: static void BreakCond_Print(bc_breakpoint_t *bp)
1273: {
1274: fprintf(stderr, "\t%s", bp->expression);
1.1.1.2 root 1275: if (bp->options.skip) {
1276: fprintf(stderr, " :%d", bp->options.skip);
1.1 root 1277: }
1.1.1.2 root 1278: if (bp->options.once) {
1.1 root 1279: fprintf(stderr, " :once");
1280: }
1.1.1.8 root 1281: if (bp->options.quiet) {
1282: fprintf(stderr, " :quiet");
1283: }
1.1.1.2 root 1284: if (bp->options.trace) {
1285: if (bp->options.lock) {
1286: fprintf(stderr, " :lock");
1287: } else {
1288: fprintf(stderr, " :trace");
1289: }
1.1.1.5 root 1290: if (bp->options.noinit) {
1291: fprintf(stderr, " :noinit");
1292: }
1.1.1.2 root 1293: }
1294: if (bp->options.filename) {
1295: fprintf(stderr, " :file %s", bp->options.filename);
1.1 root 1296: }
1.1.1.7 root 1297: if (bp->options.deleted) {
1298: fprintf(stderr, " (deleted)");
1299: }
1.1 root 1300: fprintf(stderr, "\n");
1301: }
1302:
1303: /**
1304: * List condition breakpoints
1305: */
1.1.1.7 root 1306: static void BreakCond_List(bc_breakpoints_t *bps)
1.1 root 1307: {
1308: bc_breakpoint_t *bp;
1.1.1.7 root 1309: int i;
1310:
1311: if (!bps->count) {
1312: fprintf(stderr, "No conditional %s breakpoints.\n", bps->name);
1.1 root 1313: return;
1314: }
1.1.1.7 root 1315: fprintf(stderr, "%d conditional %s breakpoints:\n", bps->count, bps->name);
1316: bp = bps->breakpoint;
1317: for (i = 1; i <= bps->count; bp++, i++) {
1.1 root 1318: fprintf(stderr, "%4d:", i);
1319: BreakCond_Print(bp);
1320: }
1321: }
1322:
1323:
1324: /**
1325: * Remove condition breakpoint at given position
1326: */
1.1.1.7 root 1327: static bool BreakCond_Remove(bc_breakpoints_t *bps, int position)
1.1 root 1328: {
1329: bc_breakpoint_t *bp;
1.1.1.2 root 1330:
1.1.1.7 root 1331: if (!bps->count) {
1332: fprintf(stderr, "No (more) %s breakpoints to remove.\n", bps->name);
1.1 root 1333: return false;
1334: }
1.1.1.7 root 1335: if (position < 1 || position > bps->count) {
1336: fprintf(stderr, "ERROR: No such %s breakpoint.\n", bps->name);
1.1 root 1337: return false;
1338: }
1.1.1.7 root 1339: bp = bps->breakpoint + (position - 1);
1340: if (bps->delayed_change) {
1341: bp->options.deleted = true;
1342: return true;
1343: }
1344: if (!bp->options.quiet) {
1345: fprintf(stderr, "Removed %s breakpoint %d:\n", bps->name, position);
1346: BreakCond_Print(bp);
1.1.1.5 root 1347: }
1.1.1.7 root 1348: free(bp->expression);
1349: free(bp->conditions);
1350: bp->expression = NULL;
1351: bp->conditions = NULL;
1.1.1.6 root 1352:
1.1.1.7 root 1353: if (bp->options.filename) {
1354: free(bp->options.filename);
1.1.1.2 root 1355: }
1.1 root 1356:
1.1.1.7 root 1357: if (position < bps->count) {
1358: memmove(bp, bp + 1, (bps->count - position) * sizeof(bc_breakpoint_t));
1.1 root 1359: }
1.1.1.7 root 1360: bps->count--;
1.1 root 1361: return true;
1362: }
1363:
1364:
1365: /**
1.1.1.7 root 1366: * Remove all conditional breakpoints
1367: */
1368: static void BreakCond_RemoveAll(bc_breakpoints_t *bps)
1369: {
1370: bool removed;
1371: int i;
1372:
1373: for (i = bps->count; i > 0; i--) {
1374: removed = BreakCond_Remove(bps, i);
1375: ASSERT_VARIABLE(removed);
1376: }
1377: fprintf(stderr, "%s breakpoints: %d\n", bps->name, bps->count);
1378: }
1379:
1380: /**
1.1.1.8 root 1381: * Do delayed breakpoint actions, remove breakpoints and old array alloc
1.1 root 1382: */
1.1.1.8 root 1383: static void BreakCond_DoDelayedActions(bc_breakpoints_t *bps)
1.1 root 1384: {
1.1.1.7 root 1385: bc_options_t *options;
1386: bool removed;
1387: int i;
1388:
1389: assert(!bps->delayed_change);
1390: if (bps->breakpoint2delete) {
1391: free(bps->breakpoint2delete);
1392: bps->breakpoint2delete = NULL;
1393: }
1394: for (i = bps->count; i > 0; i--) {
1395: options = &(bps->breakpoint[i-1].options);
1396: if (options->deleted) {
1397: options->deleted = false;
1398: removed = BreakCond_Remove(bps, i);
1399: ASSERT_VARIABLE(removed);
1400: }
1401: }
1.1 root 1402: }
1403:
1404:
1405: /**
1406: * Return true if given CPU breakpoint has given CPU expression.
1407: * Used by the test code.
1408: */
1409: int BreakCond_MatchCpuExpression(int position, const char *expression)
1410: {
1.1.1.7 root 1411: if (position < 1 || position > CpuBreakPoints.count) {
1.1 root 1412: return false;
1413: }
1.1.1.7 root 1414: if (strcmp(expression, CpuBreakPoints.breakpoint[position-1].expression)) {
1.1 root 1415: return false;
1416: }
1417: return true;
1418: }
1419:
1420:
1.1.1.8 root 1421: /* ------------- breakpoint condition parsing, public API ------------ */
1422:
1423: static const char BreakCond_Help[] =
1.1.1.2 root 1424: " condition = <value>[.mode] [& <mask>] <comparison> <value>[.mode]\n"
1.1 root 1425: "\n"
1426: " where:\n"
1427: " value = [(] <register/symbol/variable name | number> [)]\n"
1.1.1.2 root 1428: " number/mask = [#|$|%]<digits>\n"
1.1 root 1429: " comparison = '<' | '>' | '=' | '!'\n"
1430: " addressing mode (width) = 'b' | 'w' | 'l'\n"
1431: " addressing mode (space) = 'p' | 'x' | 'y'\n"
1432: "\n"
1433: " If the value is in parenthesis like in '($ff820)' or '(a0)', then\n"
1434: " the used value will be read from the memory address pointed by it.\n"
1435: "\n"
1.1.1.2 root 1436: " If the parsed value expressions on both sides of it are exactly\n"
1437: " the same, right side is replaced with its current value. For\n"
1438: " inequality ('!') comparison, the breakpoint will additionally track\n"
1439: " all further changes for the given address/register expression value.\n"
1440: " (This is useful for tracking register and memory value changes.)\n"
1.1 root 1441: "\n"
1442: " M68k addresses can have byte (b), word (w) or long (l, default) width.\n"
1443: " DSP addresses belong to different address spaces: P, X or Y. Note that\n"
1444: " on DSP only R0-R7 registers can be used for memory addressing.\n"
1445: "\n"
1446: " Examples:\n"
1447: " pc = $64543 && ($ff820).w & 3 = (a0) && d0 = %1100\n"
1448: " ($ffff9202).w ! ($ffff9202).w :trace\n"
1.1.1.8 root 1449: " (r0).x = 1 && (r0).y = 2\n"
1450: "\n"
1451: " For breakpoint options, see 'help b'.\n";
1.1 root 1452:
1453:
1454: const char BreakCond_Description[] =
1.1.1.2 root 1455: "<condition> [&& <condition> ...] [:<option>] | <index> | help | all\n"
1456: "\n"
1457: "\tSet breakpoint with given <conditions>, remove breakpoint with\n"
1458: "\tgiven <index>, remove all breakpoints with 'all' or output\n"
1459: "\tbreakpoint condition syntax with 'help'. Without arguments,\n"
1460: "\tlists currently active breakpoints.\n"
1461: "\n"
1462: "\tMultiple breakpoint action options can be specified after\n"
1463: "\tthe breakpoint condition(s):\n"
1464: "\t- 'trace', print the breakpoint match without stopping\n"
1465: "\t- 'lock', print the debugger entry info without stopping\n"
1.1.1.5 root 1466: "\t- 'noinit', no debugger inits on hit, useful for stack tracing\n"
1.1.1.2 root 1467: "\t- 'file <file>', execute debugger commands from given <file>\n"
1468: "\t- 'once', delete the breakpoint after it's hit\n"
1.1.1.5 root 1469: "\t- 'quiet', no output from setting & hitting breakpoint\n"
1.1.1.2 root 1470: "\t- '<count>', break only on every <count> hit";
1471:
1472: /**
1473: * Parse options for the given breakpoint command.
1474: * Return true for success and false for failure.
1475: */
1476: static bool BreakCond_Options(char *str, bc_options_t *options, char marker)
1477: {
1478: char *option, *next, *filename;
1479: int skip;
1480:
1481: memset(options, 0, sizeof(*options));
1482:
1483: option = strchr(str, marker);
1484: if (option) {
1485: /* end breakcond command at options */
1486: *option = 0;
1487: }
1488: for (next = option; option; option = next) {
1489:
1490: /* skip marker + end & trim this option */
1491: option = next + 1;
1492: next = strchr(option, marker);
1493: if (next) {
1494: *next = 0;
1495: }
1496: option = Str_Trim(option);
1497:
1498: if (strcmp(option, "once") == 0) {
1499: options->once = true;
1.1.1.5 root 1500: } else if (strcmp(option, "quiet") == 0) {
1501: options->quiet = true;
1.1.1.2 root 1502: } else if (strcmp(option, "trace") == 0) {
1503: options->trace = true;
1504: } else if (strcmp(option, "lock") == 0) {
1505: options->trace = true;
1506: options->lock = true;
1.1.1.5 root 1507: } else if (strcmp(option, "noinit") == 0) {
1508: options->trace = true;
1509: options->noinit = true;
1.1.1.2 root 1510: } else if (strncmp(option, "file ", 5) == 0) {
1511: filename = Str_Trim(option+4);
1512: if (!File_Exists(filename)) {
1513: fprintf(stderr, "ERROR: given file '%s' doesn't exist!\n", filename);
1514: fprintf(stderr, "(if you use 'cd' command, do it before setting breakpoints)\n");
1515: return false;
1516: }
1517: options->filename = filename;
1.1.1.6 root 1518: } else if (isdigit((unsigned char)*option)) {
1.1.1.2 root 1519: /* :<value> */
1520: skip = atoi(option);
1521: if (skip < 2) {
1522: fprintf(stderr, "ERROR: invalid breakpoint skip count '%s'!\n", option);
1523: return false;
1524: }
1525: options->skip = skip;
1526: } else {
1527: fprintf(stderr, "ERROR: unrecognized breakpoint option '%s'!\n", option);
1528: return false;
1529: }
1530: }
1531: return true;
1532: }
1.1 root 1533:
1534: /**
1535: * Parse given command expression to set/remove/list
1536: * conditional breakpoints for CPU or DSP.
1537: * Return true for success and false for failure.
1538: */
1539: bool BreakCond_Command(const char *args, bool bForDsp)
1540: {
1.1.1.7 root 1541: bc_breakpoints_t *bps;
1.1.1.2 root 1542: char *expression, *argscopy;
1.1 root 1543: unsigned int position;
1.1.1.2 root 1544: bc_options_t options;
1.1 root 1545: const char *end;
1.1.1.2 root 1546: bool ret = true;
1.1.1.7 root 1547:
1548: bps = BreakCond_GetListInfo(bForDsp);
1.1 root 1549: if (!args) {
1.1.1.7 root 1550: BreakCond_List(bps);
1.1 root 1551: return true;
1552: }
1553: argscopy = strdup(args);
1554: assert(argscopy);
1555:
1556: expression = Str_Trim(argscopy);
1557:
1.1.1.2 root 1558: /* subcommands? */
1.1 root 1559: if (strncmp(expression, "help", 4) == 0) {
1.1.1.8 root 1560: fputs(BreakCond_Help, stderr);
1.1 root 1561: goto cleanup;
1562: }
1563: if (strcmp(expression, "all") == 0) {
1.1.1.7 root 1564: BreakCond_RemoveAll(bps);
1.1 root 1565: goto cleanup;
1566: }
1567:
1568: if (bForDsp && !bDspEnabled) {
1569: ret = false;
1570: fprintf(stderr, "ERROR: DSP not enabled!\n");
1571: goto cleanup;
1572: }
1573:
1.1.1.2 root 1574: /* postfix options? */
1575: if (!BreakCond_Options(expression, &options, ':')) {
1576: ret = false;
1577: goto cleanup;
1.1 root 1578: }
1579:
1.1.1.2 root 1580: /* index (for breakcond removal)? */
1.1 root 1581: end = expression;
1.1.1.6 root 1582: while (isdigit((unsigned char)*end)) {
1.1 root 1583: end++;
1584: }
1585: if (end > expression && *end == '\0' &&
1586: sscanf(expression, "%u", &position) == 1) {
1.1.1.7 root 1587: ret = BreakCond_Remove(bps, position);
1.1 root 1588: } else {
1589: /* add breakpoint? */
1.1.1.2 root 1590: ret = BreakCond_Parse(expression, &options, bForDsp);
1.1 root 1591: }
1592: cleanup:
1593: free(argscopy);
1594: return ret;
1595: }
1596:
1597:
1598: const char BreakAddr_Description[] =
1.1.1.2 root 1599: "<address> [:<option>]\n"
1.1 root 1600: "\tCreate conditional breakpoint for given PC <address>.\n"
1.1.1.2 root 1601: "\n"
1602: "\tBreakpoint action option alternatives:\n"
1603: "\t- 'trace', print the breakpoint match without stopping\n"
1604: "\t- 'lock', print the debugger entry info without stopping\n"
1605: "\t- 'once', delete the breakpoint after it's hit\n"
1.1.1.5 root 1606: "\t- 'quiet', no output from setting & hitting breakpoint\n"
1.1.1.2 root 1607: "\t- '<count>', break only on every <count> hit\n"
1608: "\n"
1609: "\tUse conditional breakpoint commands to manage the created\n"
1.1 root 1610: "\tbreakpoints.";
1611:
1612: /**
1613: * Set CPU & DSP program counter address breakpoints by converting
1614: * them to conditional breakpoints.
1615: * Return true for success and false for failure.
1616: */
1617: bool BreakAddr_Command(char *args, bool bForDsp)
1618: {
1619: const char *errstr, *expression = (const char *)args;
1620: char *cut, command[32];
1621: Uint32 addr;
1622: int offset;
1623:
1.1.1.2 root 1624: if (!args) {
1625: if (bForDsp) {
1626: DebugUI_PrintCmdHelp("dspaddress");
1627: } else {
1628: DebugUI_PrintCmdHelp("address");
1629: }
1630: return true;
1631: }
1632:
1.1 root 1633: /* split options */
1634: if ((cut = strchr(args, ':'))) {
1635: *cut = '\0';
1636: cut = Str_Trim(cut+1);
1.1.1.2 root 1637: if (strlen(cut) > 5) {
1638: cut[5] = '\0';
1.1 root 1639: }
1640: }
1641:
1642: /* evaluate address expression */
1643: errstr = Eval_Expression(expression, &addr, &offset, bForDsp);
1644: if (errstr) {
1645: fprintf(stderr, "ERROR in the address expression:\n'%s'\n%*c-%s\n",
1646: expression, offset+2, '^', errstr);
1647: return false;
1648: }
1649:
1650: /* add the address breakpoint with optional option */
1651: sprintf(command, "pc=$%x %c%s", addr, cut?':':' ', cut?cut:"");
1652: if (!BreakCond_Command(command, bForDsp)) {
1653: return false;
1654: }
1655:
1656: /* on success, show on what instruction it was added */
1657: if (bForDsp) {
1.1.1.5 root 1658: DSP_DisasmAddress(stderr, addr, addr);
1.1 root 1659: } else {
1660: uaecptr dummy;
1.1.1.5 root 1661: Disasm(stderr, (uaecptr)addr, &dummy, 1);
1.1 root 1662: }
1663: return true;
1664: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.