--- hatari/src/debug/debugcpu.c 2019/04/09 08:48:37 1.1.1.1 +++ hatari/src/debug/debugcpu.c 2019/04/09 08:58:02 1.1.1.9 @@ -1,8 +1,8 @@ /* Hatari - debugcpu.c - 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. debugcpu.c - function needed for the CPU debugging tasks like memory and register dumps. @@ -10,6 +10,8 @@ const char DebugCpu_fileid[] = "Hatari debugcpu.c : " __DATE__ " " __TIME__; #include +#include +#include #include "config.h" @@ -21,12 +23,19 @@ const char DebugCpu_fileid[] = "Hatari d #include "debugcpu.h" #include "evaluate.h" #include "hatari-glue.h" +#include "history.h" #include "log.h" #include "m68000.h" #include "memorySnapShot.h" +#include "profile.h" #include "stMemory.h" #include "str.h" #include "symbols.h" +#include "68kDisass.h" +#include "console.h" +#include "options.h" +#include "vars.h" + #define MEMDUMP_COLS 16 /* memdump, number of bytes per row */ #define NON_PRINT_CHAR '.' /* character to display for non-printables */ @@ -34,6 +43,7 @@ const char DebugCpu_fileid[] = "Hatari d static Uint32 disasm_addr; /* disasm address */ static Uint32 memdump_addr; /* memdump address */ +static bool bCpuProfiling; /* Whether CPU profiling is activated */ static int nCpuActiveCBs = 0; /* Amount of active conditional breakpoints */ static int nCpuSteps = 0; /* Amount of steps for CPU single-stepping */ @@ -50,8 +60,7 @@ static int DebugCpu_LoadBin(int nArgc, c if (nArgc < 3) { - DebugUI_PrintCmdHelp(psArgs[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(psArgs[0]); } if (!Eval_Number(psArgs[2], &address)) @@ -59,7 +68,6 @@ static int DebugCpu_LoadBin(int nArgc, c fprintf(stderr, "Invalid address!\n"); return DEBUGGER_CMDDONE; } - address &= 0x00FFFFFF; if ((fp = fopen(psArgs[1], "rb")) == NULL) { @@ -67,6 +75,12 @@ static int DebugCpu_LoadBin(int nArgc, c return DEBUGGER_CMDDONE; } + /* TODO: more efficient would be to: + * - check file size + * - verify that it fits into valid memory area + * - flush emulated CPU data cache + * - read file contents directly into memory + */ c = fgetc(fp); while (!feof(fp)) { @@ -93,8 +107,7 @@ static int DebugCpu_SaveBin(int nArgc, c if (nArgc < 4) { - DebugUI_PrintCmdHelp(psArgs[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(psArgs[0]); } if (!Eval_Number(psArgs[2], &address)) @@ -102,7 +115,6 @@ static int DebugCpu_SaveBin(int nArgc, c fprintf(stderr, " Invalid address!\n"); return DEBUGGER_CMDDONE; } - address &= 0x00FFFFFF; if (!Eval_Number(psArgs[3], &bytes)) { @@ -130,14 +142,20 @@ static int DebugCpu_SaveBin(int nArgc, c /** - * Check whether given address matches any CPU symbol, if yes, - * show the symbol information. + * Check whether given address matches any CPU symbol and whether + * there's profiling information available for it. If yes, show it. + * + * @return true if symbol was shown, false otherwise */ -static void DebugCpu_ShowMatchedSymbol(Uint32 addr) +static bool DebugCpu_ShowAddressInfo(Uint32 addr, FILE *fp) { - const char *symbol = Symbols_GetByCpuAddress(addr); + const char *symbol = Symbols_GetByCpuAddress(addr, SYMTYPE_ALL); if (symbol) - fprintf(debugOutput, "%s:\n", symbol); + { + fprintf(fp, "%s:\n", symbol); + return true; + } + return false; } /** @@ -146,12 +164,12 @@ static void DebugCpu_ShowMatchedSymbol(U int DebugCpu_DisAsm(int nArgc, char *psArgs[]) { Uint32 disasm_upper = 0; - int insts, max_insts; + int shown, lines = INT_MAX; uaecptr nextpc; if (nArgc > 1) { - switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper)) + switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper, false)) { case -1: /* invalid value(s) */ @@ -161,7 +179,6 @@ int DebugCpu_DisAsm(int nArgc, char *psA break; case 1: /* range */ - disasm_upper &= 0x00FFFFFF; break; } } @@ -171,24 +188,20 @@ int DebugCpu_DisAsm(int nArgc, char *psA if(!disasm_addr) disasm_addr = M68000_GetPC(); } - disasm_addr &= 0x00FFFFFF; /* limit is topmost address or instruction count */ - if (disasm_upper) + if (!disasm_upper) { - max_insts = INT_MAX; - } - else - { - disasm_upper = 0x00FFFFFF; - max_insts = ConfigureParams.Debugger.nDisasmLines; + disasm_upper = 0xFFFFFFFF; + lines = DebugUI_GetPageLines(ConfigureParams.Debugger.nDisasmLines, 8); } /* output a range */ - for (insts = 0; insts < max_insts && disasm_addr < disasm_upper; insts++) + for (shown = 0; shown < lines && disasm_addr < disasm_upper; shown++) { - DebugCpu_ShowMatchedSymbol(disasm_addr); - m68k_disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1); + if (DebugCpu_ShowAddressInfo(disasm_addr, debugOutput)) + shown++; + Disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1); disasm_addr = nextpc; } fflush(debugOutput); @@ -204,32 +217,17 @@ int DebugCpu_DisAsm(int nArgc, char *psA */ static char *DebugCpu_MatchRegister(const char *text, int state) { - static const char regs[][3] = { + static const char* regs[] = { "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "pc", "sr" }; - static int i, len; - - if (!state) - { - /* first match */ - i = 0; - len = strlen(text); - if (len > 2) - return NULL; - } - /* next match */ - while (i < ARRAYSIZE(regs)) { - if (strncasecmp(regs[i++], text, len) == 0) - return (strdup(regs[i-1])); - } - return NULL; + return DebugUI_MatchHelper(regs, ARRAY_SIZE(regs), text, state); } /** - * Set address of the named register to given argument. + * Set address of the named 32-bit register to given argument. * Return register size in bits or zero for uknown register name. * Handles D0-7 data and A0-7 address registers, but not PC & SR * registers as they need to be accessed using UAE accessors. @@ -240,8 +238,8 @@ int DebugCpu_GetRegisterAddress(const ch if (!reg[0] || !reg[1] || reg[2]) return 0; - r0 = toupper(reg[0]); - r1 = toupper(reg[1]); + r0 = toupper((unsigned char)reg[0]); + r1 = toupper((unsigned char)reg[1]); if (r0 == 'D') /* Data regs? */ { @@ -281,7 +279,11 @@ int DebugCpu_Register(int nArgc, char *p { uaecptr nextpc; /* use the UAE function instead */ +#ifdef WINUAE_FOR_HATARI + m68k_dumpstate_file(debugOutput, &nextpc); +#else m68k_dumpstate(debugOutput, &nextpc); +#endif fflush(debugOutput); return DEBUGGER_CMDDONE; } @@ -305,8 +307,8 @@ int DebugCpu_Register(int nArgc, char *p { goto error_msg; } - reg[0] = toupper(arg[0]); - reg[1] = toupper(arg[1]); + reg[0] = toupper((unsigned char)arg[0]); + reg[1] = toupper((unsigned char)arg[1]); reg[2] = '\0'; /* set SR and update conditional flags for the UAE CPU core. */ @@ -340,7 +342,7 @@ error_msg: /** - * CPU wrapper for BreakAddr_Command/BreakPointCount. + * CPU wrapper for BreakAddr_Command(). */ static int DebugCpu_BreakAddr(int nArgc, char *psArgs[]) { @@ -349,7 +351,7 @@ static int DebugCpu_BreakAddr(int nArgc, } /** - * CPU wrapper for BreakCond_Command/BreakPointCount. + * CPU wrapper for BreakCond_Command(). */ static int DebugCpu_BreakCond(int nArgc, char *psArgs[]) { @@ -357,6 +359,14 @@ static int DebugCpu_BreakCond(int nArgc, return DEBUGGER_CMDDONE; } +/** + * CPU wrapper for Profile_Command(). + */ +static int DebugCpu_Profile(int nArgc, char *psArgs[]) +{ + return Profile_Command(nArgc, psArgs, false); +} + /** * Do a memory dump, args = starting address. @@ -369,7 +379,7 @@ int DebugCpu_MemDump(int nArgc, char *ps if (nArgc > 1) { - switch (Eval_Range(psArgs[1], &memdump_addr, &memdump_upper)) + switch (Eval_Range(psArgs[1], &memdump_addr, &memdump_upper, false)) { case -1: /* invalid value(s) */ @@ -382,17 +392,16 @@ int DebugCpu_MemDump(int nArgc, char *ps break; } } /* continue */ - memdump_addr &= 0x00FFFFFF; if (!memdump_upper) { - memdump_upper = memdump_addr + MEMDUMP_COLS * ConfigureParams.Debugger.nMemdumpLines; + int lines = DebugUI_GetPageLines(ConfigureParams.Debugger.nMemdumpLines, 8); + memdump_upper = memdump_addr + MEMDUMP_COLS * lines; } - memdump_upper &= 0x00FFFFFF; while (memdump_addr < memdump_upper) { - fprintf(debugOutput, "%6.6X: ", memdump_addr); /* print address */ + fprintf(debugOutput, "%8.8X: ", memdump_addr); /* print address */ for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */ fprintf(debugOutput, "%2.2x ", STMemory_ReadByte(memdump_addr++)); fprintf(debugOutput, " "); /* print ASCII data */ @@ -422,8 +431,7 @@ static int DebugCpu_MemWrite(int nArgc, if (nArgc < 3) { - DebugUI_PrintCmdHelp(psArgs[0]); - return DEBUGGER_CMDDONE; + return DebugUI_PrintCmdHelp(psArgs[0]); } /* Read address */ @@ -433,7 +441,6 @@ static int DebugCpu_MemWrite(int nArgc, return DEBUGGER_CMDDONE; } - write_addr &= 0x00FFFFFF; numBytes = 0; /* get bytes data */ @@ -479,26 +486,189 @@ static int DebugCpu_Continue(int nArgc, return DEBUGGER_END; } +/** + * Command: Single-step CPU + */ +static int DebugCpu_Step(int nArgc, char *psArgv[]) +{ + nCpuSteps = 1; + return DEBUGGER_END; +} + + +/** + * Readline match callback to list next command opcode types. + * STATE = 0 -> different text from previous one. + * Return next match or NULL if no matches. + */ +static char *DebugCpu_MatchNext(const char *text, int state) +{ + static const char* ntypes[] = { + "branch", "exception", "exreturn", "return", "subcall", "subreturn" + }; + return DebugUI_MatchHelper(ntypes, ARRAY_SIZE(ntypes), text, state); +} + +/** + * Command: Step CPU, but proceed through subroutines + * Does this by temporary conditional breakpoint + */ +static int DebugCpu_Next(int nArgc, char *psArgv[]) +{ + char command[40]; + if (nArgc > 1) + { + int optype; + if(strcmp(psArgv[1], "branch") == 0) + optype = CALL_BRANCH; + else if(strcmp(psArgv[1], "exception") == 0) + optype = CALL_EXCEPTION; + else if(strcmp(psArgv[1], "exreturn") == 0) + optype = CALL_EXCRETURN; + else if(strcmp(psArgv[1], "subcall") == 0) + optype = CALL_SUBROUTINE; + else if (strcmp(psArgv[1], "subreturn") == 0) + optype = CALL_SUBRETURN; + else if (strcmp(psArgv[1], "return") == 0) + optype = CALL_SUBRETURN | CALL_EXCRETURN; + else + { + fprintf(stderr, "Unrecognized opcode type given!\n"); + return DEBUGGER_CMDDONE; + } + sprintf(command, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype); + } + else + { + Uint32 optype, nextpc; + + optype = DebugCpu_OpcodeType(); + /* should this instruction be stepped normally, or is it + * - subroutine call + * - exception + * - loop branch backwards + */ + if (optype == CALL_SUBROUTINE || + optype == CALL_EXCEPTION || + (optype == CALL_BRANCH && + (STMemory_ReadWord(M68000_GetPC()) & 0xf0f8) == 0x50c8 && + (Sint16)STMemory_ReadWord(M68000_GetPC()+SIZE_WORD) < 0)) + { + nextpc = Disasm_GetNextPC(M68000_GetPC()); + sprintf(command, "pc=$%x :once :quiet\n", nextpc); + } + else + { + nCpuSteps = 1; + return DEBUGGER_END; + } + } + /* use breakpoint, not steps */ + if (BreakCond_Command(command, false)) + { + nCpuSteps = 0; + return DEBUGGER_END; + } + return DEBUGGER_CMDDONE; +} + +/* helper to get instruction type */ +Uint32 DebugCpu_OpcodeType(void) +{ + /* cannot use OpcodeFamily like profiler does, + * as that's for previous instructions + */ + Uint16 opcode = STMemory_ReadWord(M68000_GetPC()); + + if (opcode == 0x4e74 || /* RTD */ + opcode == 0x4e75 || /* RTS */ + opcode == 0x4e77) /* RTR */ + return CALL_SUBRETURN; + + if (opcode == 0x4e73) /* RTE */ + return CALL_EXCRETURN; + + /* NOTE: BSR needs to be matched before BRA/BCC! */ + if ((opcode & 0xff00) == 0x6100 || /* BSR */ + (opcode & 0xffc0) == 0x4e80) /* JSR */ + return CALL_SUBROUTINE; + + /* TODO: ftrapcc, chk2? */ + if (opcode == 0x4e72 || /* STOP */ + opcode == 0x4afc || /* ILLEGAL */ + opcode == 0x4e76 || /* TRAPV */ + (opcode & 0xfff0) == 0x4e40 || /* TRAP */ + (opcode & 0xf1c0) == 0x4180 || /* CHK */ + (opcode & 0xfff8) == 0x4848) /* BKPT */ + return CALL_EXCEPTION; + + /* TODO: fbcc, fdbcc */ + if ((opcode & 0xf000) == 0x6000 || /* BRA / BCC */ + (opcode & 0xffc0) == 0x4ec0 || /* JMP */ + (opcode & 0xf0f8) == 0x50c8) /* DBCC */ + return CALL_BRANCH; + + return CALL_UNKNOWN; +} + + +/** + * CPU instructions since continuing emulation + */ +static Uint32 nCpuInstructions; +Uint32 DebugCpu_InstrCount(void) +{ + return nCpuInstructions; +} /** * This function is called after each CPU instruction when debugging is enabled. */ void DebugCpu_Check(void) { - if (LOG_TRACE_LEVEL(TRACE_CPU_DISASM)) + nCpuInstructions++; + if (bCpuProfiling) { - DebugCpu_ShowMatchedSymbol(M68000_GetPC()); + Profile_CpuUpdate(); + } + if (LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS))) + { + DebugCpu_ShowAddressInfo(M68000_GetPC(), TraceFile); + } + if (LOG_TRACE_LEVEL(TRACE_CPU_REGS)) + { + uaecptr nextpc; +#ifdef WINUAE_FOR_HATARI + m68k_dumpstate_file(TraceFile, &nextpc); +#else + m68k_dumpstate(TraceFile, &nextpc); +#endif } if (nCpuActiveCBs) { if (BreakCond_MatchCpu()) - DebugUI(); + { + DebugUI(REASON_CPU_BREAKPOINT); + /* make sure we don't decrease step count + * below, before even even getting out of here + */ + if (nCpuSteps) + nCpuSteps++; + } } if (nCpuSteps) { - nCpuSteps -= 1; + nCpuSteps--; if (nCpuSteps == 0) - DebugUI(); + DebugUI(REASON_CPU_STEPS); + } + if (History_TrackCpu()) + { + History_AddCpu(); + } + if (ConOutDevice != CONOUT_DEVICE_NONE) + { + Console_Check(); } } @@ -509,9 +679,16 @@ void DebugCpu_Check(void) */ void DebugCpu_SetDebugging(void) { - nCpuActiveCBs = BreakCond_BreakPointCount(false); - if (nCpuActiveCBs || nCpuSteps) + bCpuProfiling = Profile_CpuStart(); + nCpuActiveCBs = BreakCond_CpuBreakPointCount(); + + if (nCpuActiveCBs || nCpuSteps || bCpuProfiling || History_TrackCpu() + || LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS|TRACE_CPU_REGS)) + || ConOutDevice != CONOUT_DEVICE_NONE) + { M68000_SetSpecial(SPCFLAG_DEBUGGER); + nCpuInstructions = 0; + } else M68000_UnsetSpecial(SPCFLAG_DEBUGGER); } @@ -526,7 +703,7 @@ static const dbgcommand_t cpucommands[] "set CPU PC address breakpoints", BreakAddr_Description, true }, - { DebugCpu_BreakCond, BreakCond_MatchCpuVariable, + { DebugCpu_BreakCond, Vars_MatchCpuVariable, "breakpoint", "b", "set/remove/list conditional CPU breakpoints", BreakCond_Description, @@ -538,6 +715,11 @@ static const dbgcommand_t cpucommands[] "\tIf no address is given, this command disassembles from the last\n" "\tposition or from current PC if no last position is available.", false }, + { DebugCpu_Profile, Profile_Match, + "profile", "", + "profile CPU code", + Profile_Description, + false }, { DebugCpu_Register, DebugCpu_MatchRegister, "cpureg", "r", "dump register values or set register to value", @@ -556,7 +738,7 @@ static const dbgcommand_t cpucommands[] "write bytes to memory", "address byte1 [byte2 ...]\n" "\tWrite bytes to a memory address, bytes are space separated\n" - "\thexadecimals.", + "\tvalues in current number base.", false }, { DebugCpu_LoadBin, NULL, "loadbin", "l", @@ -565,17 +747,32 @@ static const dbgcommand_t cpucommands[] "\tLoad the file into memory starting at
.", false }, { DebugCpu_SaveBin, NULL, - "savebin", "s", + "savebin", "", "save memory to a file", "filename address length\n" "\tSave the memory block at
with given to\n" "\tthe file .", false }, - { Symbols_Command, NULL, + { Symbols_Command, Symbols_MatchCommand, "symbols", "", "load CPU symbols & their addresses", Symbols_Description, false }, + { DebugCpu_Step, NULL, + "step", "s", + "single-step CPU", + "\n" + "\tExecute next CPU instruction (equals 'c 1')", + false }, + { DebugCpu_Next, DebugCpu_MatchNext, + "next", "n", + "step CPU through subroutine calls / to given instruction type", + "[instruction type]\n" + "\tSame as 'step' command if there are no subroutine calls.\n" + "\tWhen there are, those calls are treated as one instruction.\n" + "\tIf argument is given, continues until instruction of given\n" + "\ttype is encountered.", + false }, { DebugCpu_Continue, NULL, "cont", "c", "continue emulation / CPU single-stepping", @@ -602,7 +799,7 @@ int DebugCpu_Init(const dbgcommand_t **t disasm_addr = 0; *table = cpucommands; - return ARRAYSIZE(cpucommands); + return ARRAY_SIZE(cpucommands); } /** @@ -612,4 +809,5 @@ int DebugCpu_Init(const dbgcommand_t **t void DebugCpu_InitSession(void) { disasm_addr = M68000_GetPC(); + Profile_CpuStop(); }