Annotation of hatari/src/debug/debugui.c, revision 1.1.1.1

1.1       root        1: /*
                      2:   Hatari - debugui.c
                      3: 
                      4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
                      6: 
                      7:   debugui.c - this is the code for the mini-debugger. When the pause button is
                      8:   pressed, the emulator is (hopefully) halted and this little CLI can be used
                      9:   (in the terminal box) for debugging tasks like memory and register dumps.
                     10: */
                     11: const char DebugUI_fileid[] = "Hatari debugui.c : " __DATE__ " " __TIME__;
                     12: 
                     13: #include <ctype.h>
                     14: #include <stdio.h>
                     15: #include <unistd.h>
                     16: 
                     17: #include "config.h"
                     18: 
                     19: #if HAVE_LIBREADLINE
                     20: #include <readline/readline.h>
                     21: #include <readline/history.h>
                     22: #endif
                     23: 
                     24: #include "main.h"
                     25: #include "change.h"
                     26: #include "configuration.h"
                     27: #include "file.h"
                     28: #include "log.h"
                     29: #include "m68000.h"
                     30: #include "memorySnapShot.h"
                     31: #include "options.h"
                     32: #include "screen.h"
                     33: #include "statusbar.h"
                     34: #include "str.h"
                     35: 
                     36: #include "debug_priv.h"
                     37: #include "breakcond.h"
                     38: #include "debugcpu.h"
                     39: #include "debugdsp.h"
                     40: #include "debugInfo.h"
                     41: #include "debugui.h"
                     42: #include "evaluate.h"
                     43: #include "symbols.h"
                     44: 
                     45: int bExceptionDebugging;
                     46: 
                     47: FILE *debugOutput;
                     48: 
                     49: static dbgcommand_t *debugCommand;
                     50: static int debugCommands;
                     51: 
                     52: /* stores last 'e' command result as hex, used for TAB-completion */
                     53: static char lastResult[10];
                     54: 
                     55: static const char *parseFileName;
                     56: static bool DebugUI_ParseFile(const char *path);
                     57: 
                     58: 
                     59: /**
                     60:  * Save/Restore snapshot of debugging session variables
                     61:  */
                     62: void DebugUI_MemorySnapShot_Capture(const char *path, bool bSave)
                     63: {
                     64:        char *filename;
                     65: 
                     66:        filename = malloc(strlen(path) + strlen(".debug") + 1);
                     67:        assert(filename);
                     68:        strcpy(filename, path);
                     69:        strcat(filename, ".debug");
                     70:        
                     71:        if (bSave)
                     72:        {
                     73:                /* save breakpoints as debugger input file */
                     74:                BreakCond_Save(filename);
                     75:        }
                     76:        else
                     77:        {
                     78:                /* remove current CPU and DSP breakpoints */
                     79:                BreakCond_Command("all", false);
                     80:                BreakCond_Command("all", true);
                     81: 
                     82:                if (File_Exists(filename))
                     83:                {
                     84:                        /* and parse back the saved breakpoints */
                     85:                        DebugUI_ParseFile(filename);
                     86:                }
                     87:        }
                     88:        free(filename);
                     89: }
                     90: 
                     91: 
                     92: /**
                     93:  * Close a log file if open, and set it to default stream.
                     94:  */
                     95: static void DebugUI_SetLogDefault(void)
                     96: {
                     97:        if (debugOutput != stderr)
                     98:        {
                     99:                if (debugOutput)
                    100:                {
                    101:                        File_Close(debugOutput);
                    102:                        fprintf(stderr, "Debug log closed.\n");
                    103:                }
                    104:                debugOutput = stderr;
                    105:        }
                    106: }
                    107: 
                    108: 
                    109: /**
                    110:  * Open (or close) given log file.
                    111:  */
                    112: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
                    113: {
                    114:        File_Close(debugOutput);
                    115:        debugOutput = NULL;
                    116: 
                    117:        if (nArgc > 1)
                    118:                debugOutput = File_Open(psArgs[1], "w");
                    119: 
                    120:        if (debugOutput)
                    121:                fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
                    122:        else
                    123:                debugOutput = stderr;
                    124: 
                    125:        return DEBUGGER_CMDDONE;
                    126: }
                    127: 
                    128: 
                    129: /**
                    130:  * Helper to print given value in all supported number bases
                    131:  */
                    132: static void DebugUI_PrintValue(Uint32 value)
                    133: {
                    134:        bool one, ones;
                    135:        int bit;
                    136: 
                    137:        fputs("= %", stderr);
                    138:        ones = false;
                    139:        for (bit = 31; bit >= 0; bit--)
                    140:        {
                    141:                one = value & (1<<bit);
                    142:                if (one || ones)
                    143:                {
                    144:                        fputc(one ? '1':'0', stderr);
                    145:                        ones = true;
                    146:                }
                    147:        }
                    148:        if (!ones)
                    149:                fputc('0', stderr);
                    150:        if (value & 0x80000000)
                    151:                fprintf(stderr, " (bin), #%u/%d (dec), $%x (hex)\n", value, (int)value, value);
                    152:        else
                    153:                fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
                    154: 
                    155:        sprintf(lastResult, "%x", value);
                    156: }
                    157: 
                    158: 
                    159: /**
                    160:  * Commmand: Evaluate an expression with CPU reg and symbol parsing.
                    161:  */
                    162: static int DebugUI_Evaluate(int nArgc, char *psArgs[])
                    163: {
                    164:        const char *errstr, *expression = (const char *)psArgs[1];
                    165:        Uint32 result;
                    166:        int offset;
                    167: 
                    168:        if (nArgc < 2)
                    169:        {
                    170:                DebugUI_PrintCmdHelp(psArgs[0]);
                    171:                return DEBUGGER_CMDDONE;
                    172:        }
                    173: 
                    174:        errstr = Eval_Expression(expression, &result, &offset, false);
                    175:        if (errstr)
                    176:                fprintf(stderr, "ERROR in the expression:\n'%s'\n%*c-%s\n",
                    177:                        expression, offset+3, '^', errstr);
                    178:        else
                    179:                DebugUI_PrintValue(result);
                    180:        return DEBUGGER_CMDDONE;
                    181: }
                    182: 
                    183: 
                    184: /**
                    185:  * Check whether given string is a two letter command starting with 'd'
                    186:  * or a long command starting with "dsp". String should be trimmed.
                    187:  * Return true if given string is command for DSP, false otherwise.
                    188:  */
                    189: static bool DebugUI_IsForDsp(const char *cmd)
                    190: {
                    191:        return ((cmd[0] == 'd' && cmd[1] && !cmd[2])
                    192:                || strncmp(cmd, "dsp", 3) == 0);
                    193: }
                    194: 
                    195: /**
                    196:  * Evaluate everything include within "" and replace them with the result.
                    197:  * String given as an argument needs to be allocated, it may be freed and
                    198:  * re-allocated if it needs to be expanded. Caller needs to free the returned
                    199:  * string if it's not NULL.
                    200:  * 
                    201:  * Return string with expressions expanded or NULL for an error.
                    202:  */
                    203: static char *DebugUI_EvaluateExpressions(char *input)
                    204: {
                    205:        int offset, count, diff, inputlen;
                    206:        char *end, *start;
                    207:        const char *errstr;
                    208:        char valuestr[12];
                    209:        Uint32 value;
                    210:        bool fordsp;
                    211: 
                    212:        /* input is split later on, need to save len here */
                    213:        fordsp = DebugUI_IsForDsp(input);
                    214:        inputlen = strlen(input);
                    215:        start = input;
                    216:        
                    217:        while ((start = strchr(start, '"')))
                    218:        {
                    219:                end = strchr(start+1, '"');
                    220:                if (!end)
                    221:                {
                    222:                        fprintf(stderr, "ERROR: matching '\"' missing from '%s'!\n", start);
                    223:                        free(input);
                    224:                        return NULL;
                    225:                }
                    226:                
                    227:                if (end == start+1)
                    228:                {
                    229:                        /* empty expression */
                    230:                        memmove(start, start+2, strlen(start+2)+1);
                    231:                        continue;
                    232:                }
                    233: 
                    234:                *end = '\0';
                    235:                errstr = Eval_Expression(start+1, &value, &offset, fordsp);
                    236:                if (errstr) {
                    237:                        *end = '"';
                    238:                        fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n",
                    239:                                input, (int)(start-input)+offset+3, '^', errstr);
                    240:                        free(input);
                    241:                        return NULL;
                    242:                }
                    243:                end++;
                    244:                
                    245:                count = sprintf(valuestr, "$%x", value);
                    246:                fprintf(stderr, "- \"%s\" -> %s\n", start+1, valuestr);
                    247: 
                    248:                diff = end-start;
                    249:                if (count < diff)
                    250:                {
                    251:                        memcpy(start, valuestr, count);
                    252:                        start += count;
                    253:                        memmove(start, end, strlen(end) + 1);
                    254:                } else {
                    255:                        /* value won't fit to expression, expand string */
                    256:                        char *tmp;
                    257:                        inputlen += count-diff+1;
                    258:                        tmp = malloc(inputlen+1);
                    259:                        if (!tmp)
                    260:                        {
                    261:                                perror("ERROR: Input string alloc failed\n");
                    262:                                free(input);
                    263:                                return NULL;
                    264:                        }
                    265: 
                    266:                        memcpy(tmp, input, start-input);
                    267:                        start = tmp+(start-input);
                    268:                        memcpy(start, valuestr, count);
                    269:                        start += count;
                    270:                        memcpy(start, end, strlen(end) + 1);
                    271: 
                    272:                        free(input);
                    273:                        input = tmp;
                    274:                }
                    275:        }
                    276:        /* no (more) expressions to evaluate */
                    277:        return input;
                    278: }
                    279: 
                    280: 
                    281: /**
                    282:  * Command: Store and restore emulation state
                    283:  */
                    284: static int DebugUI_DoMemorySnap(int argc, char *argv[])
                    285: {
                    286:        const char *file;
                    287: 
                    288:        if (argc > 1)
                    289:                file = argv[1];
                    290:        else
                    291:                file = ConfigureParams.Memory.szMemoryCaptureFileName;
                    292: 
                    293:        if (strcmp(argv[0], "stateload") == 0)
                    294:                MemorySnapShot_Restore(file, true);
                    295:        else
                    296:                MemorySnapShot_Capture(file, true);
                    297: 
                    298:        return DEBUGGER_CMDDONE;
                    299: }
                    300: 
                    301: 
                    302: /**
                    303:  * Command: Set command line and debugger options
                    304:  */
                    305: static int DebugUI_SetOptions(int argc, char *argv[])
                    306: {
                    307:        CNF_PARAMS current;
                    308:        static const struct {
                    309:                const char name[4];
                    310:                int base;
                    311:        } bases[] = {
                    312:                { "bin", 2 },
                    313:                { "dec", 10 },
                    314:                { "hex", 16 }
                    315:        };
                    316:        const char *arg;
                    317:        int i;
                    318:        
                    319:        if (argc < 2)
                    320:        {
                    321:                DebugUI_PrintCmdHelp(argv[0]);
                    322:                return DEBUGGER_CMDDONE;
                    323:        }
                    324:        arg = argv[1];
                    325:        
                    326:        for (i = 0; i < ARRAYSIZE(bases); i++)
                    327:        {
                    328:                if (strcasecmp(bases[i].name, arg) == 0)
                    329:                {
                    330:                        if (ConfigureParams.Debugger.nNumberBase != bases[i].base)
                    331:                        {
                    332:                                fprintf(stderr, "Switched default number base from %d to %d-based (%s) values.\n",
                    333:                                        ConfigureParams.Debugger.nNumberBase,
                    334:                                        bases[i].base, bases[i].name);
                    335:                                ConfigureParams.Debugger.nNumberBase = bases[i].base;
                    336:                        } else {
                    337:                                fprintf(stderr, "Already in '%s' mode.\n", bases[i].name);
                    338:                        }
                    339:                        return DEBUGGER_CMDDONE;
                    340:                }
                    341:        }
                    342: 
                    343:        /* get configuration changes */
                    344:        current = ConfigureParams;
                    345: 
                    346:        /* Parse and apply options */
                    347:        if (Opt_ParseParameters(argc, (const char**)argv))
                    348:        {
                    349:                ConfigureParams.Screen.bFullScreen = false;
                    350:                Change_CopyChangedParamsToConfiguration(&current, &ConfigureParams, false);
                    351:        }
                    352:        else
                    353:        {
                    354:                ConfigureParams = current;
                    355:        }
                    356: 
                    357:        return DEBUGGER_CMDDONE;
                    358: }
                    359: 
                    360: 
                    361: /**
                    362:  * Command: Set tracing
                    363:  */
                    364: static int DebugUI_SetTracing(int argc, char *argv[])
                    365: {
                    366:        const char *errstr;
                    367:        if (argc != 2)
                    368:        {
                    369:                DebugUI_PrintCmdHelp(argv[0]);
                    370:                return DEBUGGER_CMDDONE;
                    371:        }
                    372:        errstr = Log_SetTraceOptions(argv[1]);
                    373:        if (errstr && errstr[0])
                    374:                fprintf(stderr, "ERROR: %s\n", errstr);
                    375: 
                    376:        return DEBUGGER_CMDDONE;
                    377: }
                    378: 
                    379: 
                    380: /**
                    381:  * Command: Change Hatari work directory
                    382:  */
                    383: static int DebugUI_ChangeDir(int argc, char *argv[])
                    384: {
                    385:        if (argc == 2)
                    386:        {
                    387:                if (chdir(argv[1]) == 0)
                    388:                        return DEBUGGER_CMDDONE;
                    389:                perror("ERROR");
                    390:        }
                    391:        DebugUI_PrintCmdHelp(argv[0]);
                    392:        return DEBUGGER_CMDDONE;
                    393: }
                    394: 
                    395: 
                    396: /**
                    397:  * Command: Read debugger commands from a file
                    398:  */
                    399: static int DebugUI_CommandsFromFile(int argc, char *argv[])
                    400: {
                    401:        if (argc == 2)
                    402:                DebugUI_ParseFile(argv[1]);
                    403:        else
                    404:                DebugUI_PrintCmdHelp(argv[0]);
                    405:        return DEBUGGER_CMDDONE;
                    406: }
                    407: 
                    408: 
                    409: /**
                    410:  * Command: Execute a system command
                    411:  */
                    412: #if ENABLE_SYSTEM_DEBUG_CALL   /* Disabled by default - could be a security risk? */
                    413: static int DebugUI_Exec(int argc, char *argv[])
                    414: {
                    415:        if (argc == 2)
                    416:        {
                    417:                int ret = system(argv[1]);
                    418:                if (ret)
                    419:                {
                    420:                        /* error -> show return code */
                    421:                        fprintf(stderr, "Error code = %d\n", ret);
                    422:                }
                    423:        }
                    424:        else
                    425:                DebugUI_PrintCmdHelp(argv[0]);
                    426:        return DEBUGGER_CMDDONE;
                    427: }
                    428: #endif
                    429: 
                    430: 
                    431: /**
                    432:  * Command: Quit emulator
                    433:  */
                    434: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
                    435: {
                    436:        bQuitProgram = true;
                    437:        M68000_SetSpecial(SPCFLAG_BRK);   /* Assure that CPU core shuts down */
                    438:        return DEBUGGER_END;
                    439: }
                    440: 
                    441: 
                    442: /**
                    443:  * Print help text for one command
                    444:  */
                    445: void DebugUI_PrintCmdHelp(const char *psCmd)
                    446: {
                    447:        dbgcommand_t *cmd;
                    448:        int i;
                    449: 
                    450:        /* Search the command ... */
                    451:        for (cmd = debugCommand, i = 0; i < debugCommands; i++, cmd++)
                    452:        {
                    453:                if (!debugCommand[i].pFunction)
                    454:                        continue;
                    455:                if ((*(cmd->sShortName) && !strcmp(psCmd, cmd->sShortName))
                    456:                    || !strcmp(psCmd, cmd->sLongName))
                    457:                {
                    458:                        bool bShort = *(cmd->sShortName);
                    459:                        /* ... and print help text */
                    460:                        if (bShort)
                    461:                        {
                    462:                                fprintf(stderr, "'%s' or '%s' - %s\n",
                    463:                                        cmd->sLongName,
                    464:                                        cmd->sShortName,
                    465:                                        cmd->sShortDesc);
                    466:                        }
                    467:                        else
                    468:                        {
                    469:                                fprintf(stderr, "'%s' - %s\n",
                    470:                                        cmd->sLongName,
                    471:                                        cmd->sShortDesc);
                    472:                        }
                    473:                        fprintf(stderr, "Usage:  %s %s\n",
                    474:                                bShort ? cmd->sShortName : cmd->sLongName,
                    475:                                cmd->sUsage);
                    476:                        return;
                    477:                }
                    478:        }
                    479: 
                    480:        fprintf(stderr, "Unknown command '%s'\n", psCmd);
                    481: }
                    482: 
                    483: 
                    484: /**
                    485:  * Command: Print debugger help screen.
                    486:  */
                    487: static int DebugUI_Help(int nArgc, char *psArgs[])
                    488: {
                    489:        int i;
                    490: 
                    491:        if (nArgc > 1)
                    492:        {
                    493:                DebugUI_PrintCmdHelp(psArgs[1]);
                    494:                return DEBUGGER_CMDDONE;
                    495:        }
                    496: 
                    497:        for (i = 0; i < debugCommands; i++)
                    498:        {
                    499:                if (!debugCommand[i].pFunction)
                    500:                {
                    501:                        fprintf(stderr, "\n%s:\n", debugCommand[i].sLongName);
                    502:                        continue;
                    503:                }
                    504:                fprintf(stderr, " %12s (%2s) : %s\n", debugCommand[i].sLongName,
                    505:                        debugCommand[i].sShortName, debugCommand[i].sShortDesc);
                    506:        }
                    507: 
                    508:        fprintf(stderr,
                    509:                "\n"
                    510:                "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
                    511:                "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
                    512:                "be skipped for numbers in the default number base (currently %d).\n"
                    513:                "\n"
                    514:                "Any expression given in quotes (within \"\"), will be evaluated\n"
                    515:                "before given to the debugger command.  Any register and symbol\n"
                    516:                "names in the expression are replaced by their values.\n"
                    517:                "\n"
                    518:                "Note that address ranges like '$fc0000-$fc0100' should have no\n"
                    519:                "spaces between the range numbers.\n"
                    520:                "\n"
                    521:                "'help <command>' gives more help.\n", ConfigureParams.Debugger.nNumberBase);
                    522:        return DEBUGGER_CMDDONE;
                    523: }
                    524: 
                    525: 
                    526: /**
                    527:  * Parse debug command and execute it.
                    528:  */
                    529: static int DebugUI_ParseCommand(char *input)
                    530: {
                    531:        char *psArgs[64];
                    532:        const char *delim;
                    533:        static char sLastCmd[80] = { '\0' };
                    534:        int nArgc, cmd = -1;
                    535:        int i, retval;
                    536: 
                    537:        psArgs[0] = strtok(input, " \t");
                    538: 
                    539:        if (psArgs[0] == NULL)
                    540:        {
                    541:                if (strlen(sLastCmd) > 0)
                    542:                        psArgs[0] = sLastCmd;
                    543:                else
                    544:                        return DEBUGGER_CMDDONE;
                    545:        }
                    546: 
                    547:        /* Search the command ... */
                    548:        for (i = 0; i < debugCommands; i++)
                    549:        {
                    550:                if (!debugCommand[i].pFunction)
                    551:                        continue;
                    552:                if (!strcmp(psArgs[0], debugCommand[i].sShortName) ||
                    553:                    !strcmp(psArgs[0], debugCommand[i].sLongName))
                    554:                {
                    555:                        cmd = i;
                    556:                        break;
                    557:                }
                    558:        }
                    559:        if (cmd == -1)
                    560:        {
                    561:                fprintf(stderr, "Command '%s' not found.\n"
                    562:                        "Use 'help' to view a list of available commands.\n",
                    563:                        psArgs[0]);
                    564:                return DEBUGGER_CMDDONE;
                    565:        }
                    566: 
                    567:        if (debugCommand[cmd].bNoParsing)
                    568:                delim = "";
                    569:        else
                    570:                delim = " \t";
                    571: 
                    572:        /* Separate arguments and put the pointers into psArgs */
                    573:        for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++)
                    574:        {
                    575:                psArgs[nArgc] = strtok(NULL, delim);
                    576:                if (psArgs[nArgc] == NULL)
                    577:                        break;
                    578:        }
                    579: 
                    580:        if (!debugOutput) {
                    581:                /* make sure also calls from control.c work */
                    582:                DebugUI_SetLogDefault();
                    583:        }
                    584: 
                    585:        /* ... and execute the function */
                    586:        retval = debugCommand[i].pFunction(nArgc, psArgs);
                    587:        /* Save commando string if it can be repeated */
                    588:        if (retval == DEBUGGER_CMDCONT)
                    589:                strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
                    590:        else
                    591:                sLastCmd[0] = '\0';
                    592:        return retval;
                    593: }
                    594: 
                    595: 
                    596: /* See "info:readline" e.g. in Konqueror for readline usage. */
                    597: 
                    598: /**
                    599:  * Readline match callback for long command name completion.
                    600:  * STATE = 0 -> different text from previous one.
                    601:  * Return next match or NULL if no matches.
                    602:  */
                    603: static char *DebugUI_MatchCommand(const char *text, int state)
                    604: {
                    605:        static int i, len;
                    606:        const char *name;
                    607:        
                    608:        if (!state)
                    609:        {
                    610:                /* first match */
                    611:                len = strlen(text);
                    612:                i = 0;
                    613:        }
                    614:        /* next match */
                    615:        while (i < debugCommands)
                    616:        {
                    617:                name = debugCommand[i].sLongName;
                    618:                if (debugCommand[i++].pFunction &&
                    619:                    strncmp(name, text, len) == 0)
                    620:                        return (strdup(name));
                    621:        }
                    622:        return NULL;
                    623: }
                    624: 
                    625: 
                    626: #if HAVE_LIBREADLINE
                    627: /**
                    628:  * Readline match callback returning last result.
                    629:  */
                    630: static char *DebugUI_MatchLast(const char *text, int state)
                    631: {
                    632:        if (state)
                    633:                return NULL;
                    634:        return strdup(lastResult);
                    635: }
                    636: 
                    637: /**
                    638:  * Readline completion callback. Returns matches.
                    639:  */
                    640: static char **DebugUI_Completion(const char *text, int a, int b)
                    641: {
                    642:        int i, cmd, quotes, end, start = 0;
                    643:        char *str, buf[32];
                    644:        size_t len;
                    645: 
                    646:        /* check where's the first word (ignore white space) */
                    647:        while (start < rl_point && isspace(rl_line_buffer[start]))
                    648:                start++;
                    649:        end = start;
                    650:        while (end < rl_point && !isspace(rl_line_buffer[end]))
                    651:                end++;
                    652: 
                    653:        if (end >= rl_point)
                    654:                /* first word on line */
                    655:                return rl_completion_matches(text, DebugUI_MatchCommand);
                    656:        
                    657:        /* complete '$' with last result? */
                    658:        if (lastResult[0] && rl_line_buffer[rl_point-1] == '$')
                    659:                return rl_completion_matches(text, DebugUI_MatchLast);
                    660: 
                    661:        /* check which command args are to be completed */
                    662:        len = end - start;
                    663:        if (len >= sizeof(buf))
                    664:                len = sizeof(buf)-1;
                    665:        memcpy(buf, &(rl_line_buffer[start]), len);
                    666:        buf[len] = '\0';
                    667: 
                    668:        /* expression completion needed (= open quote)? */
                    669:        str = strchr(&(rl_line_buffer[end]), '"');
                    670:        quotes = 0;
                    671:        while (str)
                    672:        {
                    673:                quotes++;
                    674:                str = strchr(str+1, '"');
                    675:        }
                    676:        if (quotes & 1)
                    677:        {
                    678:                if (DebugUI_IsForDsp(buf))
                    679:                        return rl_completion_matches(text, Symbols_MatchDspAddress);
                    680:                return rl_completion_matches(text, Symbols_MatchCpuAddress);
                    681:        }
                    682: 
                    683:        /* do command argument completion */
                    684:        cmd = -1;
                    685:        for (i = 0; i < debugCommands; i++)
                    686:        {
                    687:                if (!debugCommand[i].pFunction)
                    688:                        continue;
                    689:                if (!strcmp(buf, debugCommand[i].sShortName) ||
                    690:                    !strcmp(buf, debugCommand[i].sLongName))
                    691:                {
                    692:                        cmd = i;
                    693:                        break;
                    694:                }
                    695:        }
                    696:        if (cmd < 0)
                    697:        {
                    698:                rl_attempted_completion_over = true;
                    699:                return NULL;
                    700:        }
                    701:        if (debugCommand[cmd].pMatch)
                    702:                return rl_completion_matches(text, debugCommand[cmd].pMatch);
                    703:        else
                    704:                return rl_completion_matches(text, rl_filename_completion_function);
                    705: }
                    706: 
                    707: 
                    708: /**
                    709:  * Read a command line from the keyboard and return a pointer to the string.
                    710:  * @return     Pointer to the string which should be deallocated free()
                    711:  *              after use. Returns NULL when error occured.
                    712:  */
                    713: static char *DebugUI_GetCommand(void)
                    714: {
                    715:        char *input;
                    716: 
                    717:        /* Allow conditional parsing of the ~/.inputrc file. */
                    718:        rl_readline_name = "Hatari";
                    719:        
                    720:        /* Tell the completer that we want a crack first. */
                    721:        rl_attempted_completion_function = DebugUI_Completion;
                    722: 
                    723:        input = readline("> ");
                    724:        if (!input)
                    725:                return NULL;
                    726: 
                    727:        input = Str_Trim(input);
                    728:        if (input[0])
                    729:                add_history(input);
                    730: 
                    731:        return input;
                    732: }
                    733: 
                    734: #else /* !HAVE_LIBREADLINE */
                    735: 
                    736: /**
                    737:  * Read a command line from the keyboard and return a pointer to the string.
                    738:  * @return     Pointer to the string which should be deallocated free()
                    739:  *              after use. Returns NULL when error occured.
                    740:  */
                    741: static char *DebugUI_GetCommand(void)
                    742: {
                    743:        char *input;
                    744:        fprintf(stderr, "> ");
                    745:        input = malloc(256);
                    746:        if (!input)
                    747:                return NULL;
                    748:        input[0] = '\0';
                    749:        if (fgets(input, 256, stdin) == NULL)
                    750:        {
                    751:                free(input);
                    752:                return NULL;
                    753:        }
                    754:        return Str_Trim(input);
                    755: }
                    756: 
                    757: #endif /* !HAVE_LIBREADLINE */
                    758: 
                    759: 
                    760: static const dbgcommand_t uicommand[] =
                    761: {
                    762:        { NULL, NULL, "Generic commands", NULL, NULL, NULL, false },
                    763:        /* NULL as match function will complete file names */
                    764:        { DebugUI_ChangeDir, NULL,
                    765:          "cd", "",
                    766:          "change directory",
                    767:          "<directory>\n"
                    768:          "\tChange Hatari work directory.",
                    769:          false },
                    770:        { DebugUI_Evaluate, Symbols_MatchCpuAddress,
                    771:          "evaluate", "e",
                    772:          "evaluate an expression",
                    773:          "<expression>\n"
                    774:          "\tEvaluate an expression and show the result.  Expression can\n"
                    775:          "\tinclude CPU register and symbol names, those are replaced\n"
                    776:          "\tby their values. Supported operators in expressions are,\n"
                    777:          "\tin the decending order of precedence:\n"
                    778:          "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n"
                    779:          "\tFor example:\n"
                    780:          "\t\t((0x21 * 0x200) + (-5)) ^ (~%111 & $f0f0f0)\n"
                    781:          "\tResult value is shown as binary, decimal and hexadecimal.\n"
                    782:          "\tAfter this, '$' will TAB-complete to last result value.",
                    783:          true },
                    784: #if ENABLE_SYSTEM_DEBUG_CALL
                    785:        { DebugUI_Exec, NULL,
                    786:          "exec", "",
                    787:          "execute a shell command",
                    788:          "<command line>\n"
                    789:          "\tRun the given command with the system shell.",
                    790:          true },
                    791: #endif
                    792:        { DebugUI_Help, DebugUI_MatchCommand,
                    793:          "help", "h",
                    794:          "print help",
                    795:          "[command]\n"
                    796:          "\tPrint help text for available commands.",
                    797:          false },
                    798:        { DebugInfo_Command, DebugInfo_MatchInfo,
                    799:          "info", "i",
                    800:          "show machine/OS information",
                    801:          "[subject [arg]]\n"
                    802:          "\tPrint information on requested subject or list them if\n"
                    803:          "\tno subject given.",
                    804:          false },
                    805:        { DebugInfo_Command, DebugInfo_MatchLock,
                    806:          "lock", "",
                    807:          "lock info to show on entering debugger",
                    808:          "[subject [args]]\n"
                    809:          "\tLock requested information to be shown every time debugger\n"
                    810:          "\tis entered or list available options if no subject's given.",
                    811:          false },
                    812:        { DebugUI_SetLogFile, NULL,
                    813:          "logfile", "f",
                    814:          "open or close log file",
                    815:          "[filename]\n"
                    816:          "\tOpen log file, no argument closes the log file. Output of\n"
                    817:          "\tregister & memory dumps and disassembly will be written to it.",
                    818:          false },
                    819:        { DebugUI_CommandsFromFile, NULL,
                    820:          "parse", "p",
                    821:          "get debugger commands from file",
                    822:          "[filename]\n"
                    823:          "\tRead debugger commands from given file and do them.",
                    824:          false },
                    825:        { DebugUI_SetOptions, Opt_MatchOption,
                    826:          "setopt", "o",
                    827:          "set Hatari command line and debugger options",
                    828:          "[<bin|dec|hex>|<command line options>]\n"
                    829:          "\tSet Hatari options. For example to enable exception catching,\n"
                    830:          "\tuse following command line option: 'setopt --debug'. Special\n"
                    831:          "\t'bin', 'dec' and 'hex' arguments change the default number base\n"
                    832:          "\tused in debugger.",
                    833:          false },
                    834:        { DebugUI_DoMemorySnap, NULL,
                    835:          "stateload", "",
                    836:          "restore emulation state",
                    837:          "[filename]\n"
                    838:          "\tRestore emulation snapshot from default or given file",
                    839:          false },
                    840:        { DebugUI_DoMemorySnap, NULL,
                    841:          "statesave", "",
                    842:          "save emulation state",
                    843:          "[filename]\n"
                    844:          "\tSave emulation snapshot to default or given file",
                    845:          false },
                    846:        { DebugUI_SetTracing, Log_MatchTrace,
                    847:          "trace", "t",
                    848:          "select Hatari tracing settings",
                    849:          "[set1,set2...]\n"
                    850:          "\tSelect Hatari tracing settings. For example to enable CPU\n"
                    851:          "\tdisassembly and VBL tracing, use:  trace cpu_disasm,video_hbl",
                    852:          false },
                    853:        { DebugUI_QuitEmu, NULL,
                    854:          "quit", "q",
                    855:          "quit emulator",
                    856:          "\n"
                    857:          "\tLeave debugger and quit emulator.",
                    858:          false }
                    859: };
                    860: 
                    861: 
                    862: /**
                    863:  * Debugger user interface initialization.
                    864:  */
                    865: void DebugUI_Init(void)
                    866: {
                    867:        const dbgcommand_t *cpucmd, *dspcmd;
                    868:        int cpucmds, dspcmds;
                    869: 
                    870:        /* already intialized? */
                    871:        if (debugCommands)
                    872:                return;
                    873: 
                    874:        /* if you want disassembly or memdumping to start/continue from
                    875:         * specific address, you can set them in these functions.
                    876:         */
                    877:        dspcmds = DebugDsp_Init(&dspcmd);
                    878:        cpucmds = DebugCpu_Init(&cpucmd);
                    879: 
                    880:        /* on first time copy the command structures to a single table */
                    881:        debugCommands = ARRAYSIZE(uicommand);
                    882:        debugCommand = malloc(sizeof(dbgcommand_t) * (dspcmds + cpucmds + debugCommands));
                    883:        assert(debugCommand);
                    884:        
                    885:        memcpy(debugCommand, uicommand, sizeof(dbgcommand_t) * debugCommands);
                    886:        memcpy(&debugCommand[debugCommands], cpucmd, sizeof(dbgcommand_t) * cpucmds);
                    887:        debugCommands += cpucmds;
                    888:        memcpy(&debugCommand[debugCommands], dspcmd, sizeof(dbgcommand_t) * dspcmds);
                    889:        debugCommands += dspcmds;
                    890: 
                    891:        if (parseFileName)
                    892:                DebugUI_ParseFile(parseFileName);
                    893: }
                    894: 
                    895: 
                    896: /**
                    897:  * Set debugger commands file.
                    898:  * Return true if file exists, false otherwise.
                    899:  */
                    900: bool DebugUI_SetParseFile(const char *path)
                    901: {
                    902:        if (File_Exists(path))
                    903:        {
                    904:                parseFileName = path;
                    905:                return true;
                    906:        }
                    907:        fprintf(stderr, "ERROR: debugger input file '%s' missing.\n", path);
                    908:        return false;
                    909: }
                    910: 
                    911: 
                    912: /**
                    913:  * Debugger user interface main function.
                    914:  */
                    915: void DebugUI(void)
                    916: {
                    917:        int cmdret, alertLevel;
                    918:        char *psCmd;
                    919:        static const char *welcome =
                    920:                "\n----------------------------------------------------------------------"
                    921:                "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
                    922:        
                    923:        if (bInFullScreen)
                    924:                Screen_ReturnFromFullScreen();
                    925: 
                    926:        DebugUI_Init();
                    927: 
                    928:        if (welcome)
                    929:        {
                    930:                fputs(welcome, stderr);
                    931:                welcome = NULL;
                    932:        }
                    933:        DebugCpu_InitSession();
                    934:        DebugDsp_InitSession();
                    935:        DebugInfo_ShowSessionInfo();
                    936: 
                    937:        /* override paused message so that user knows to look into console
                    938:         * on how to continue in case he invoked the debugger by accident.
                    939:         */
                    940:        Statusbar_AddMessage("Console Debugger", 100);
                    941:        Statusbar_Update(sdlscrn);
                    942: 
                    943:        /* disable normal GUI alerts while on console */
                    944:        alertLevel = Log_SetAlertLevel(LOG_FATAL);
                    945: 
                    946:        cmdret = DEBUGGER_CMDDONE;
                    947:        do
                    948:        {
                    949:                /* Read command from the keyboard */
                    950:                psCmd = DebugUI_GetCommand();
                    951:                if (!psCmd)
                    952:                        break;
                    953: 
                    954:                /* expand all quoted expressions */
                    955:                if (!(psCmd = DebugUI_EvaluateExpressions(psCmd)))
                    956:                        continue;
                    957: 
                    958:                /* Parse and execute the command string */
                    959:                cmdret = DebugUI_ParseCommand(psCmd);
                    960:                free(psCmd);
                    961:        }
                    962:        while (cmdret != DEBUGGER_END);
                    963: 
                    964:        Log_SetAlertLevel(alertLevel);
                    965:        DebugUI_SetLogDefault();
                    966: 
                    967:        DebugCpu_SetDebugging();
                    968:        DebugDsp_SetDebugging();
                    969: }
                    970: 
                    971: 
                    972: /**
                    973:  * Read debugger commands from a file.
                    974:  * return false for error, true for success.
                    975:  */
                    976: static bool DebugUI_ParseFile(const char *path)
                    977: {
                    978:        char *dir, *cmd, *input, *slash;
                    979:        FILE *fp;
                    980: 
                    981:        fprintf(stderr, "Reading debugger commands from '%s'...\n", path);
                    982:        if (!(fp = fopen(path, "r")))
                    983:        {
                    984:                perror("ERROR");
                    985:                return false;
                    986:        }
                    987: 
                    988:        /* change to directory where the debugger file resides */
                    989:        dir = strdup(path);
                    990:        slash = strrchr(dir, PATHSEP);
                    991:        if (slash)
                    992:        {
                    993:                *slash = '\0';
                    994:                if (chdir(dir))
                    995:                {
                    996:                        perror("ERROR");
                    997:                        free(dir);
                    998:                        return false;
                    999:                }
                   1000:        }
                   1001:        free(dir);
                   1002: 
                   1003:        input = NULL;
                   1004:        for (;;)
                   1005:        {
                   1006:                if (!input)
                   1007:                {
                   1008:                        input = malloc(256);
                   1009:                        assert(input);
                   1010:                }
                   1011:                if (!fgets(input, 256, fp))
                   1012:                        break;
                   1013: 
                   1014:                input = DebugUI_EvaluateExpressions(input);
                   1015:                if (!input)
                   1016:                        continue;
                   1017: 
                   1018:                cmd = Str_Trim(input);
                   1019:                if (*cmd && *cmd != '#')
                   1020:                {
                   1021:                        fprintf(stderr, "> %s\n", cmd);
                   1022:                        DebugUI_ParseCommand(cmd);
                   1023:                }
                   1024:        }
                   1025: 
                   1026:        free(input);
                   1027: 
                   1028:        DebugCpu_SetDebugging();
                   1029:        DebugDsp_SetDebugging();
                   1030:        return true;
                   1031: }
                   1032: 
                   1033: 
                   1034: /**
                   1035:  * Remote/parallel debugger usage API.
                   1036:  * Return false for failed command, true for success.
                   1037:  */
                   1038: bool DebugUI_RemoteParse(char *input)
                   1039: {
                   1040:        int ret;
                   1041: 
                   1042:        DebugUI_Init();
                   1043:        
                   1044:        ret = DebugUI_ParseCommand(input);
                   1045: 
                   1046:        DebugCpu_SetDebugging();
                   1047:        DebugDsp_SetDebugging();
                   1048: 
                   1049:        return (ret == DEBUGGER_CMDDONE);
                   1050: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.