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