--- hatari/src/debug/breakcond.c 2019/04/09 08:50:20 1.1.1.3 +++ hatari/src/debug/breakcond.c 2019/04/09 08:54:20 1.1.1.6 @@ -1,10 +1,10 @@ /* Hatari - breakcond.c - Copyright (c) 2009-2010 by Eero Tamminen + Copyright (c) 2009-2012 by Eero Tamminen - This file is distributed under the GNU Public License, version 2 or at - your option any later version. Read the file gpl.txt for details. + This file is distributed under the GNU General Public License, version 2 + or at your option any later version. Read the file gpl.txt for details. breakcond.c - code for breakpoint conditions that can check variable and memory values against each other, mask them etc. before deciding @@ -29,6 +29,7 @@ const char BreakCond_fileid[] = "Hatari #include "debug_priv.h" #include "breakcond.h" #include "debugcpu.h" +#include "debugdsp.h" #include "debugInfo.h" #include "debugui.h" #include "evaluate.h" @@ -43,9 +44,6 @@ const char BreakCond_fileid[] = "Hatari /* needs to go through long long to handle x=32 */ #define BITMASK(x) ((Uint32)(((unsigned long long)1<<(x))-1)) -#define BC_MAX_CONDITION_BREAKPOINTS 16 -#define BC_MAX_CONDITIONS_PER_BREAKPOINT 4 - #define BC_DEFAULT_DSP_SPACE 'P' typedef enum { @@ -94,22 +92,24 @@ typedef struct { char *filename; /* file where to read commands to do on hit */ int skip; /* how many times to hit before breaking */ bool once; /* remove after hit&break */ + bool quiet; /* no output from setting & hitting */ bool trace; /* trace mode, don't break */ + bool noinit; /* prevent debugger inits on break */ bool lock; /* tracing + show locked info */ } bc_options_t; typedef struct { char *expression; bc_options_t options; - bc_condition_t conditions[BC_MAX_CONDITIONS_PER_BREAKPOINT]; + bc_condition_t *conditions; int ccount; /* condition count */ int hits; /* how many times breakpoint hit */ } bc_breakpoint_t; -static bc_breakpoint_t BreakPointsCpu[BC_MAX_CONDITION_BREAKPOINTS]; -static bc_breakpoint_t BreakPointsDsp[BC_MAX_CONDITION_BREAKPOINTS]; -static int BreakPointCpuCount; -static int BreakPointDspCount; +static bc_breakpoint_t *BreakPointsCpu; +static bc_breakpoint_t *BreakPointsDsp; +static int BreakPointCpuCount, BreakPointCpuAllocated; +static int BreakPointDspCount, BreakPointDspAllocated; /* forward declarations */ @@ -246,9 +246,31 @@ static Uint32 BreakCond_GetValue(const b /** + * Show & update rvalue for a tracked breakpoint condition to lvalue + */ +static void BreakCond_UpdateTracked(bc_condition_t *condition, Uint32 value) +{ + Uint32 addr; + + /* next monitor changes to this new value */ + condition->rvalue.value.number = value; + + if (condition->lvalue.is_indirect && + condition->lvalue.valuetype == VALUE_TYPE_NUMBER) { + /* simple memory address */ + addr = condition->lvalue.value.number; + fprintf(stderr, " $%x = $%x\n", addr, value); + } else { + /* register tms. */ + fprintf(stderr, " $%x\n", value); + } +} + + +/** * Return true if all of the given breakpoint's conditions match */ -static bool BreakCond_MatchConditions(const bc_condition_t *condition, int count) +static bool BreakCond_MatchConditions(bc_condition_t *condition, int count) { Uint32 lvalue, rvalue; bool hit = false; @@ -280,6 +302,9 @@ static bool BreakCond_MatchConditions(co if (!hit) { return false; } + if (condition->track) { + BreakCond_UpdateTracked(condition, lvalue); + } } /* all conditions matched */ return true; @@ -287,61 +312,31 @@ static bool BreakCond_MatchConditions(co /** - * Show values for the tracked breakpoint conditions - */ -static void BreakCond_ShowTracked(bc_condition_t *condition, int count) -{ - Uint32 addr, value; - char sep; - int i; - - sep = ' '; - for (i = 0; i < count; condition++, i++) { - if (!condition->track) { - continue; - } - - /* get the new value in address */ - value = BreakCond_GetValue(&(condition->lvalue)); - /* next monitor changes to this new value */ - condition->rvalue.value.number = value; - - if (condition->lvalue.is_indirect && - condition->lvalue.valuetype == VALUE_TYPE_NUMBER) { - /* simple memory address */ - addr = condition->lvalue.value.number; - fprintf(stderr, "%c $%x = $%x", sep, addr, value); - } else { - /* register tms. */ - fprintf(stderr, "%c $%x", sep, value); - } - sep = ','; - } - fprintf(stderr, "\n"); -} - - -/** - * Return which of the given condition breakpoints match - * or zero if none matched + * Show all breakpoints which conditions matched and return which matched + * @return index to last matching (non-tracing) breakpoint, + * or zero if none matched */ static int BreakCond_MatchBreakPoints(bc_breakpoint_t *bp, int count, const char *name) { - bool for_dsp; - int i, ret; - + int i, ret = 0; + for (i = 0; i < count; bp++, i++) { if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) { + bool for_dsp; bp->hits++; - if (bp->options.skip && - (bp->hits % bp->options.skip) == 0) { - return 0; + if (bp->options.skip) { + if (bp->hits % bp->options.skip) { + /* check next */ + continue; + } + } + if (!bp->options.quiet) { + fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n", + i+1, name, bp->hits); + BreakCond_Print(bp); } - fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n", - i+1, name, bp->hits); - for_dsp = (bp-i == BreakPointsDsp); if (for_dsp) { History_Mark(REASON_DSP_BREAKPOINT); @@ -349,31 +344,33 @@ static int BreakCond_MatchBreakPoints(bc History_Mark(REASON_CPU_BREAKPOINT); } - if (bp->options.lock) { - DebugCpu_InitSession(); - DebugDsp_InitSession(); - DebugInfo_ShowSessionInfo(); - } - if (bp->options.filename) { - DebugUI_ParseFile(bp->options.filename); - } + if (bp->options.lock || bp->options.filename) { + bool reinit = !bp->options.noinit; - if (bp->options.trace) { - ret = 0; - } else { - /* indexes for BreakCond_Remove() start from 1 */ - ret = i + 1; + if (reinit) { + DebugCpu_InitSession(); + DebugDsp_InitSession(); + } + + if (bp->options.lock) { + DebugInfo_ShowSessionInfo(); + } + if (bp->options.filename) { + DebugUI_ParseFile(bp->options.filename, reinit); + } } - - BreakCond_Print(bp); - BreakCond_ShowTracked(bp->conditions, bp->ccount); if (bp->options.once) { BreakCond_Remove(i+1, for_dsp); + count--; } - return ret; + if (!bp->options.trace) { + /* index for current hit (BreakCond_Remove() indexes start from 1) */ + ret = i + 1; + } + /* continue checking breakpoints to make sure all relevant actions get performed */ } } - return 0; + return ret; } /* ------------- breakpoint condition checking, public API ------------- */ @@ -531,19 +528,32 @@ static Uint32 GetVdiOpcode(void) return INVALID_OPCODE; } +static Uint32 GetNextPC(void) +{ + return Disasm_GetNextPC(M68000_GetPC()); +} + /* sorted by variable name so that this can be bisected */ static const var_addr_t hatari_vars[] = { { "AesOpcode", (Uint32*)GetAesOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, { "BiosOpcode", (Uint32*)GetBiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, { "BSS", (Uint32*)DebugInfo_GetBSS, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, + { "CpuInstr", (Uint32*)DebugCpu_InstrCount, VALUE_TYPE_FUNCTION32, 0, "CPU instructions count" }, + { "CpuOpcodeType", (Uint32*)DebugCpu_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "CPU instruction type" }, { "DATA", (Uint32*)DebugInfo_GetDATA, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, +#if ENABLE_DSP_EMU + { "DspInstr", (Uint32*)DebugDsp_InstrCount, VALUE_TYPE_FUNCTION32, 0, "DSP instructions count" }, + { "DspOpcodeType", (Uint32*)DebugDsp_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "DSP instruction type" }, +#endif { "FrameCycles", (Uint32*)GetFrameCycles, VALUE_TYPE_FUNCTION32, 0, NULL }, { "GemdosOpcode", (Uint32*)GetGemdosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, { "HBL", (Uint32*)&nHBL, VALUE_TYPE_VAR32, sizeof(nHBL)*8, NULL }, { "LineAOpcode", (Uint32*)GetLineAOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, { "LineCycles", (Uint32*)GetLineCycles, VALUE_TYPE_FUNCTION32, 0, "is always divisable by 4" }, { "LineFOpcode", (Uint32*)GetLineFOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, + { "NextPC", (Uint32*)GetNextPC, VALUE_TYPE_FUNCTION32, 0, NULL }, { "TEXT", (Uint32*)DebugInfo_GetTEXT, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, + { "TEXTEnd", (Uint32*)DebugInfo_GetTEXTEnd, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, { "VBL", (Uint32*)&nVBLs, VALUE_TYPE_VAR32, sizeof(nVBLs)*8, NULL }, { "VdiOpcode", (Uint32*)GetVdiOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, { "XbiosOpcode", (Uint32*)GetXbiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" } @@ -714,7 +724,8 @@ static bool BreakCond_ParseRegister(cons &(bc_value->value.reg32), &(bc_value->mask)); if (regsize) { - if (bc_value->is_indirect && toupper(regname[0]) != 'R') { + if (bc_value->is_indirect + && toupper((unsigned char)regname[0]) != 'R') { fprintf(stderr, "ERROR: only R0-R7 DSP registers can be used for indirect addressing!\n"); EXITFUNC(("-> false (DSP)\n")); return false; @@ -737,7 +748,7 @@ static bool BreakCond_ParseRegister(cons return true; } /* Exact UAE core 32-bit PC & 16-bit SR register values - * can be gotten only through AUE accessors, not directly + * can be gotten only through UAE accessors, not directly */ if (strcasecmp(regname, "PC") == 0) { bc_value->bits = 32; @@ -823,7 +834,7 @@ static bool BreakCond_ParseAddressModifi case 'p': case 'x': case 'y': - mode = toupper(pstate->argv[pstate->arg][0]); + mode = toupper((unsigned char)pstate->argv[pstate->arg][0]); break; default: pstate->error = "invalid address space modifier"; @@ -927,7 +938,7 @@ static bool BreakCond_ParseValue(parser_ } str = pstate->argv[pstate->arg]; - if (isalpha(*str) || *str == '_') { + if (isalpha((unsigned char)*str) || *str == '_') { /* parse direct or indirect variable/register/symbol name */ if (bc_value->is_indirect) { /* a valid register or data symbol name? */ @@ -1107,16 +1118,11 @@ static bool BreakCond_CrossCheckValues(p * Return number of added conditions or zero for failure. */ static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp, - bc_condition_t *conditions, int ccount) + bc_breakpoint_t *bp, int ccount) { bc_condition_t condition; ENTERFUNC(("BreakCond_ParseCondition(...)\n")); - if (ccount >= BC_MAX_CONDITIONS_PER_BREAKPOINT) { - pstate->error = "max number of conditions exceeded"; - EXITFUNC(("-> 0 (no conditions free)\n")); - return 0; - } /* setup condition */ memset(&condition, 0, sizeof(bc_condition_t)); @@ -1145,12 +1151,19 @@ static int BreakCond_ParseCondition(pars EXITFUNC(("-> 0\n")); return 0; } - /* new condition */ - conditions[ccount++] = condition; + /* copy new condition */ + ccount += 1; + bp->conditions = realloc(bp->conditions, sizeof(bc_condition_t)*(ccount)); + if (!bp->conditions) { + pstate->error = "failed to allocate space for breakpoint condition"; + EXITFUNC(("-> 0\n")); + return 0; + } + bp->conditions[ccount-1] = condition; /* continue with next condition? */ if (pstate->arg == pstate->argc) { - EXITFUNC(("-> %d (conditions)\n", ccount-1)); + EXITFUNC(("-> %d conditions (all args parsed)\n", ccount)); return ccount; } if (strcmp(pstate->argv[pstate->arg], "&&") != 0) { @@ -1161,12 +1174,12 @@ static int BreakCond_ParseCondition(pars pstate->arg++; /* recurse conditions parsing */ - ccount = BreakCond_ParseCondition(pstate, bForDsp, conditions, ccount); + ccount = BreakCond_ParseCondition(pstate, bForDsp, bp, ccount); if (!ccount) { EXITFUNC(("-> 0\n")); return 0; } - EXITFUNC(("-> %d (conditions)\n", ccount-1)); + EXITFUNC(("-> %d conditions (recursed)\n", ccount)); return ccount; } @@ -1206,7 +1219,7 @@ static char *BreakCond_TokenizeExpressio has_comparison = false; for (src = expression; *src; src++) { /* discard white space in source */ - if (isspace(*src)) { + if (isspace((unsigned char)*src)) { continue; } /* separate tokens with single space in destination */ @@ -1234,7 +1247,7 @@ static char *BreakCond_TokenizeExpressio /* validate & copy other characters */ if (!sep) { /* variable/register/symbol or number prefix? */ - if (!(isalnum(*src) || *src == '_' || + if (!(isalnum((unsigned char)*src) || *src == '_' || *src == '$' || *src == '#' || *src == '%')) { pstate->error = "invalid character"; pstate->arg = src-expression; @@ -1291,22 +1304,41 @@ static char *BreakCond_TokenizeExpressio /** - * Helper to set corrent breakpoint list and type name to given variables. + * Set corrent breakpoint list and name for given CPU type. + * Make sure there's always space for at least one additional breakpoint. * Return pointer to breakpoint list count */ static int* BreakCond_GetListInfo(bc_breakpoint_t **bp, const char **name, bool bForDsp) { - int *bcount; + int *allocated, *bcount; if (bForDsp) { + allocated = &BreakPointDspAllocated; bcount = &BreakPointDspCount; *bp = BreakPointsDsp; *name = "DSP"; } else { + allocated = &BreakPointCpuAllocated; bcount = &BreakPointCpuCount; *bp = BreakPointsCpu; *name = "CPU"; } + /* allocate (more) space for breakpoints when needed */ + if (*bcount + 1 >= *allocated) { + if (!*allocated) { + /* initial count of available breakpoints */ + *allocated = 16; + } else { + *allocated *= 2; + } + *bp = realloc(*bp, *allocated * sizeof(bc_breakpoint_t)); + assert(*bp); + if (bForDsp) { + BreakPointsDsp = *bp; + } else { + BreakPointsCpu = *bp; + } + } return bcount; } @@ -1336,8 +1368,8 @@ static void BreakCond_CheckTracking(bc_b condition->rvalue.value.number = value; condition->rvalue.valuetype = VALUE_TYPE_NUMBER; condition->rvalue.is_indirect = false; - if (condition->comparison == '!') { - /* which changes will be traced */ + /* track those changes */ + if (condition->comparison != '=') { condition->track = true; track = true; } else { @@ -1366,18 +1398,23 @@ static bool BreakCond_Parse(const char * int ccount; bcount = BreakCond_GetListInfo(&bp, &name, bForDsp); - if (*bcount >= BC_MAX_CONDITION_BREAKPOINTS) { - fprintf(stderr, "ERROR: no free %s condition breakpoints left.\n", name); - return false; - } + bp += *bcount; memset(bp, 0, sizeof(bc_breakpoint_t)); normalized = BreakCond_TokenizeExpression(expression, &pstate); if (normalized) { bp->expression = normalized; - ccount = BreakCond_ParseCondition(&pstate, bForDsp, - bp->conditions, 0); + ccount = BreakCond_ParseCondition(&pstate, bForDsp, bp, 0); + /* fail? */ + if (!ccount) { + bp->expression = NULL; + if (bp->conditions) { + /* free what was allocated by ParseCondition */ + free(bp->conditions); + bp->conditions = NULL; + } + } bp->ccount = ccount; } else { ccount = 0; @@ -1387,27 +1424,37 @@ static bool BreakCond_Parse(const char * } if (ccount > 0) { (*bcount)++; - fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n", - name, *bcount, ccount, bp->expression); - BreakCond_CheckTracking(bp); - if (options->skip) { - fprintf(stderr, "-> Break only on every %d hit.\n", options->skip); - bp->options.skip = options->skip; - } - if (options->once) { - fprintf(stderr, "-> Once, delete after breaking.\n"); - bp->options.once = true; - } - if (options->trace) { - fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n"); - if (options->lock) { - fprintf(stderr, "-> Show also info selected with lock command.\n"); - bp->options.lock = true; + if (!options->quiet) { + fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n", + name, *bcount, ccount, bp->expression); + if (options->skip) { + fprintf(stderr, "-> Break only on every %d hit.\n", options->skip); + } + if (options->once) { + fprintf(stderr, "-> Once, delete after breaking.\n"); + } + if (options->trace) { + fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n"); + if (options->lock) { + fprintf(stderr, "-> Show also info selected with lock command.\n"); + } + if (options->noinit) { + fprintf(stderr, "-> Skip debugger inits on hit.\n"); + } + } + if (options->filename) { + fprintf(stderr, "-> Execute debugger commands from '%s' file on hit.\n", options->filename); } - bp->options.trace = true; } + BreakCond_CheckTracking(bp); + + bp->options.quiet = options->quiet; + bp->options.skip = options->skip; + bp->options.once = options->once; + bp->options.trace = options->trace; + bp->options.lock = options->lock; + bp->options.noinit = options->noinit; if (options->filename) { - fprintf(stderr, "-> Execute debugger commands from '%s' file on hit.\n", options->filename); bp->options.filename = strdup(options->filename); } } else { @@ -1426,7 +1473,6 @@ static bool BreakCond_Parse(const char * fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n", normalized, offset+2, '^', pstate.error); free(normalized); - bp->expression = NULL; } else { /* show original string and point out the character * where the error was encountered @@ -1457,6 +1503,9 @@ static void BreakCond_Print(bc_breakpoin } else { fprintf(stderr, " :trace"); } + if (bp->options.noinit) { + fprintf(stderr, " :noinit"); + } } if (bp->options.filename) { fprintf(stderr, " :file %s", bp->options.filename); @@ -1506,13 +1555,18 @@ static bool BreakCond_Remove(int positio return false; } offset = position - 1; - fprintf(stderr, "Removed %s breakpoint %d:\n", name, position); - BreakCond_Print(&(bp[offset])); + if (!bp[offset].options.quiet) { + fprintf(stderr, "Removed %s breakpoint %d:\n", name, position); + BreakCond_Print(&(bp[offset])); + } free(bp[offset].expression); + free(bp[offset].conditions); + bp[offset].expression = NULL; + bp[offset].conditions = NULL; + if (bp[offset].options.filename) { free(bp[offset].options.filename); } - bp[offset].expression = NULL; if (position < *bcount) { memmove(bp+offset, bp+position, @@ -1623,8 +1677,10 @@ const char BreakCond_Description[] = "\tthe breakpoint condition(s):\n" "\t- 'trace', print the breakpoint match without stopping\n" "\t- 'lock', print the debugger entry info without stopping\n" + "\t- 'noinit', no debugger inits on hit, useful for stack tracing\n" "\t- 'file ', execute debugger commands from given \n" "\t- 'once', delete the breakpoint after it's hit\n" + "\t- 'quiet', no output from setting & hitting breakpoint\n" "\t- '', break only on every hit"; /** @@ -1655,11 +1711,16 @@ static bool BreakCond_Options(char *str, if (strcmp(option, "once") == 0) { options->once = true; + } else if (strcmp(option, "quiet") == 0) { + options->quiet = true; } else if (strcmp(option, "trace") == 0) { options->trace = true; } else if (strcmp(option, "lock") == 0) { options->trace = true; options->lock = true; + } else if (strcmp(option, "noinit") == 0) { + options->trace = true; + options->noinit = true; } else if (strncmp(option, "file ", 5) == 0) { filename = Str_Trim(option+4); if (!File_Exists(filename)) { @@ -1668,7 +1729,7 @@ static bool BreakCond_Options(char *str, return false; } options->filename = filename; - } else if (isdigit(*option)) { + } else if (isdigit((unsigned char)*option)) { /* : */ skip = atoi(option); if (skip < 2) { @@ -1730,7 +1791,7 @@ bool BreakCond_Command(const char *args, /* index (for breakcond removal)? */ end = expression; - while (isdigit(*end)) { + while (isdigit((unsigned char)*end)) { end++; } if (end > expression && *end == '\0' && @@ -1754,6 +1815,7 @@ const char BreakAddr_Description[] = "\t- 'trace', print the breakpoint match without stopping\n" "\t- 'lock', print the debugger entry info without stopping\n" "\t- 'once', delete the breakpoint after it's hit\n" + "\t- 'quiet', no output from setting & hitting breakpoint\n" "\t- '', break only on every hit\n" "\n" "\tUse conditional breakpoint commands to manage the created\n" @@ -1805,10 +1867,10 @@ bool BreakAddr_Command(char *args, bool /* on success, show on what instruction it was added */ if (bForDsp) { - DSP_DisasmAddress(addr, addr); + DSP_DisasmAddress(stderr, addr, addr); } else { uaecptr dummy; - Disasm(stderr, (uaecptr)addr, &dummy, 1, DISASM_ENGINE_EXT); + Disasm(stderr, (uaecptr)addr, &dummy, 1); } return true; }