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