1 |
/* |
/* |
2 |
* Copyright (C) 2004-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2004-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: debugger.c,v 1.14 2006/07/01 21:15:46 debug Exp $ |
* $Id: debugger.c,v 1.22 2007/03/26 02:01:36 debug Exp $ |
29 |
* |
* |
30 |
* Single-step debugger. |
* Single-step debugger. |
31 |
* |
* |
38 |
* |
* |
39 |
* Call stack display (back-trace)? |
* Call stack display (back-trace)? |
40 |
* |
* |
|
* More generic expression evaluator (for example + - * / between multiple |
|
|
* terms), including _TAB COMPLETION_ of symbols and register names! |
|
|
* Must be possible to reach any emul, any machine in any emul, any |
|
|
* cpu in any machine, other variables in the emulator, and so forth. |
|
|
* Perhaps a "settable variables registry" somewhere? |
|
|
* |
|
41 |
* Nicer looking output of register dumps, floating point registers, |
* Nicer looking output of register dumps, floating point registers, |
42 |
* etc. Warn about weird/invalid register contents. |
* etc. Warn about weird/invalid register contents. |
43 |
* |
* |
57 |
#include "cpu.h" |
#include "cpu.h" |
58 |
#include "device.h" |
#include "device.h" |
59 |
#include "debugger.h" |
#include "debugger.h" |
|
#include "debugger_gdb.h" |
|
60 |
#include "diskimage.h" |
#include "diskimage.h" |
61 |
#include "emul.h" |
#include "emul.h" |
62 |
#include "machine.h" |
#include "machine.h" |
64 |
#include "misc.h" |
#include "misc.h" |
65 |
#include "net.h" |
#include "net.h" |
66 |
#include "settings.h" |
#include "settings.h" |
67 |
|
#include "timer.h" |
68 |
#include "x11.h" |
#include "x11.h" |
69 |
|
|
70 |
|
|
100 |
|
|
101 |
static int debugger_n_emuls; |
static int debugger_n_emuls; |
102 |
static struct emul **debugger_emuls; |
static struct emul **debugger_emuls; |
103 |
static struct emul *debugger_emul; |
|
104 |
|
/* Currently focused CPU, machine, and emulation: */ |
105 |
|
int debugger_cur_cpu; |
106 |
|
int debugger_cur_machine; |
107 |
|
int debugger_cur_emul; |
108 |
static struct machine *debugger_machine; |
static struct machine *debugger_machine; |
109 |
|
static struct emul *debugger_emul; |
110 |
|
|
111 |
#define MAX_CMD_BUFLEN 72 |
#define MAX_CMD_BUFLEN 72 |
112 |
#define N_PREVIOUS_CMDS 150 |
#define N_PREVIOUS_CMDS 150 |
126 |
*/ |
*/ |
127 |
char debugger_readchar(void) |
char debugger_readchar(void) |
128 |
{ |
{ |
129 |
int ch, i, j; |
int ch; |
130 |
|
|
131 |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0 && !exit_debugger) { |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0 && !exit_debugger) { |
132 |
/* Check for X11 events: */ |
/* Check for X11 events: */ |
133 |
x11_check_event(debugger_emuls, debugger_n_emuls); |
x11_check_event(debugger_emuls, debugger_n_emuls); |
134 |
|
|
|
/* Check for incoming GDB packets: */ |
|
|
for (i=0; i<debugger_n_emuls; i++) { |
|
|
struct emul *e = debugger_emuls[i]; |
|
|
if (e == NULL) |
|
|
continue; |
|
|
|
|
|
for (j=0; j<e->n_machines; j++) { |
|
|
if (e->machines[j]->gdb.port > 0) |
|
|
debugger_gdb_check_incoming( |
|
|
e->machines[j]); |
|
|
} |
|
|
} |
|
|
|
|
|
/* TODO: The X11 and GDB checks above should probably |
|
|
be factored out... */ |
|
|
|
|
135 |
/* Give up some CPU time: */ |
/* Give up some CPU time: */ |
136 |
usleep(1); |
usleep(10000); |
137 |
} |
} |
138 |
return ch; |
return ch; |
139 |
} |
} |
177 |
|
|
178 |
|
|
179 |
/* |
/* |
|
* debugger_parse_name(): |
|
|
* |
|
|
* This function reads a string, and tries to match it to a register name, |
|
|
* a symbol, or treat it as a decimal numeric value. |
|
|
* |
|
|
* Some examples: |
|
|
* |
|
|
* "0x7fff1234" ==> numeric value (hex, in this case) |
|
|
* "pc", "r5", "hi", "t4" ==> register (CPU dependent) |
|
|
* "memcpy+64" ==> symbol (plus offset) |
|
|
* |
|
|
* Register names can be preceeded by "x:" where x is the CPU number. (CPU |
|
|
* 0 is assumed by default.) |
|
|
* |
|
|
* To force detection of different types, a character can be added in front of |
|
|
* the name: "$" for numeric values, "%" for registers, and "@" for symbols. |
|
|
* |
|
|
* Return value is: |
|
|
* |
|
|
* NAME_PARSE_NOMATCH no match |
|
|
* NAME_PARSE_MULTIPLE multiple matches |
|
|
* |
|
|
* or one of these (and then *valuep is read or written, depending on |
|
|
* the writeflag): |
|
|
* |
|
|
* NAME_PARSE_REGISTER a register |
|
|
* NAME_PARSE_NUMBER a hex number |
|
|
* NAME_PARSE_SYMBOL a symbol |
|
|
*/ |
|
|
#define NAME_PARSE_NOMATCH 0 |
|
|
#define NAME_PARSE_MULTIPLE 1 |
|
|
#define NAME_PARSE_REGISTER 2 |
|
|
#define NAME_PARSE_NUMBER 3 |
|
|
#define NAME_PARSE_SYMBOL 4 |
|
|
static int debugger_parse_name(struct machine *m, char *name, int writeflag, |
|
|
uint64_t *valuep) |
|
|
{ |
|
|
int match_register = 0, match_symbol = 0, match_numeric = 0; |
|
|
int skip_register, skip_numeric, skip_symbol; |
|
|
|
|
|
if (m == NULL || name == NULL) { |
|
|
fprintf(stderr, "debugger_parse_name(): NULL ptr\n"); |
|
|
exit(1); |
|
|
} |
|
|
|
|
|
/* Warn about non-signextended values: */ |
|
|
if (writeflag) { |
|
|
if (m->cpus[0]->is_32bit) { |
|
|
/* Automagically sign-extend. TODO: Is this good? */ |
|
|
if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL) |
|
|
(*valuep) |= 0xffffffff00000000ULL; |
|
|
} else { |
|
|
if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL) |
|
|
printf("WARNING: The value is not sign-extende" |
|
|
"d. Is this what you intended?\n"); |
|
|
} |
|
|
} |
|
|
|
|
|
skip_register = name[0] == '$' || name[0] == '@'; |
|
|
skip_numeric = name[0] == '%' || name[0] == '@'; |
|
|
skip_symbol = name[0] == '$' || name[0] == '%'; |
|
|
|
|
|
/* Check for a register match: */ |
|
|
if (!skip_register && strlen(name) >= 1) |
|
|
cpu_register_match(m, name, writeflag, valuep, |
|
|
&match_register); |
|
|
|
|
|
/* Check for a number match: */ |
|
|
if (!skip_numeric && isdigit((int)name[0])) { |
|
|
uint64_t x; |
|
|
x = strtoull(name, NULL, 0); |
|
|
if (writeflag) { |
|
|
printf("You cannot assign like that.\n"); |
|
|
} else |
|
|
*valuep = x; |
|
|
match_numeric = 1; |
|
|
} |
|
|
|
|
|
/* Check for a symbol match: */ |
|
|
if (!skip_symbol) { |
|
|
int res; |
|
|
char *p, *sn; |
|
|
uint64_t newaddr, ofs = 0; |
|
|
|
|
|
sn = malloc(strlen(name) + 1); |
|
|
if (sn == NULL) { |
|
|
fprintf(stderr, "out of memory in debugger\n"); |
|
|
exit(1); |
|
|
} |
|
|
strlcpy(sn, name, strlen(name)+1); |
|
|
|
|
|
/* Is there a '+' in there? Then treat that as an offset: */ |
|
|
p = strchr(sn, '+'); |
|
|
if (p != NULL) { |
|
|
*p = '\0'; |
|
|
ofs = strtoull(p+1, NULL, 0); |
|
|
} |
|
|
|
|
|
res = get_symbol_addr(&m->symbol_context, sn, &newaddr); |
|
|
if (res) { |
|
|
if (writeflag) { |
|
|
printf("You cannot assign like that.\n"); |
|
|
} else |
|
|
*valuep = newaddr + ofs; |
|
|
match_symbol = 1; |
|
|
} |
|
|
|
|
|
free(sn); |
|
|
} |
|
|
|
|
|
if (match_register + match_symbol + match_numeric > 1) |
|
|
return NAME_PARSE_MULTIPLE; |
|
|
|
|
|
if (match_register) |
|
|
return NAME_PARSE_REGISTER; |
|
|
if (match_numeric) |
|
|
return NAME_PARSE_NUMBER; |
|
|
if (match_symbol) |
|
|
return NAME_PARSE_SYMBOL; |
|
|
|
|
|
return NAME_PARSE_NOMATCH; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
180 |
* show_breakpoint(): |
* show_breakpoint(): |
181 |
*/ |
*/ |
182 |
static void show_breakpoint(struct machine *m, int i) |
static void show_breakpoint(struct machine *m, int i) |
239 |
|
|
240 |
/* printf("left = '%s'\nright = '%s'\n", left, right); */ |
/* printf("left = '%s'\nright = '%s'\n", left, right); */ |
241 |
|
|
242 |
res_right = debugger_parse_name(m, right, 0, &tmp); |
res_right = debugger_parse_expression(m, right, 0, &tmp); |
243 |
switch (res_right) { |
switch (res_right) { |
244 |
case NAME_PARSE_NOMATCH: |
case PARSE_NOMATCH: |
245 |
printf("No match for the right-hand side of the assignment.\n"); |
printf("No match for the right-hand side of the assignment.\n"); |
246 |
break; |
break; |
247 |
case NAME_PARSE_MULTIPLE: |
case PARSE_MULTIPLE: |
248 |
printf("Multiple matches for the right-hand side of the " |
printf("Multiple matches for the right-hand side of the " |
249 |
"assignment.\n"); |
"assignment.\n"); |
250 |
break; |
break; |
251 |
default: |
default: |
252 |
res_left = debugger_parse_name(m, left, 1, &tmp); |
res_left = debugger_parse_expression(m, left, 1, &tmp); |
253 |
switch (res_left) { |
switch (res_left) { |
254 |
case NAME_PARSE_NOMATCH: |
case PARSE_NOMATCH: |
255 |
printf("No match for the left-hand side of the " |
printf("No match for the left-hand side of the " |
256 |
"assignment.\n"); |
"assignment.\n"); |
257 |
break; |
break; |
258 |
case NAME_PARSE_MULTIPLE: |
case PARSE_MULTIPLE: |
259 |
printf("Multiple matches for the left-hand side " |
printf("Multiple matches for the left-hand side " |
260 |
"of the assignment.\n"); |
"of the assignment.\n"); |
261 |
break; |
break; |
568 |
} else if (ch == 27) { |
} else if (ch == 27) { |
569 |
/* Escape codes: (cursor keys etc) */ |
/* Escape codes: (cursor keys etc) */ |
570 |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) |
571 |
usleep(1); |
usleep(10000); |
572 |
if (ch == '[' || ch == 'O') { |
if (ch == '[' || ch == 'O') { |
573 |
while ((ch = console_readchar(MAIN_CONSOLE)) |
while ((ch = console_readchar(MAIN_CONSOLE)) |
574 |
< 0) |
< 0) |
575 |
usleep(1); |
usleep(10000); |
576 |
switch (ch) { |
switch (ch) { |
577 |
case '2': /* 2~ = ins */ |
case '2': /* 2~ = ins */ |
578 |
case '5': /* 5~ = pgup */ |
case '5': /* 5~ = pgup */ |
580 |
/* TODO: Ugly hack, but might work. */ |
/* TODO: Ugly hack, but might work. */ |
581 |
while ((ch = console_readchar( |
while ((ch = console_readchar( |
582 |
MAIN_CONSOLE)) < 0) |
MAIN_CONSOLE)) < 0) |
583 |
usleep(1); |
usleep(10000); |
584 |
/* Do nothing for these keys. */ |
/* Do nothing for these keys. */ |
585 |
break; |
break; |
586 |
case '3': /* 3~ = delete */ |
case '3': /* 3~ = delete */ |
587 |
/* TODO: Ugly hack, but might work. */ |
/* TODO: Ugly hack, but might work. */ |
588 |
while ((ch = console_readchar( |
while ((ch = console_readchar( |
589 |
MAIN_CONSOLE)) < 0) |
MAIN_CONSOLE)) < 0) |
590 |
usleep(1); |
usleep(10000); |
591 |
console_makeavail(MAIN_CONSOLE, '\b'); |
console_makeavail(MAIN_CONSOLE, '\b'); |
592 |
break; |
break; |
593 |
case 'A': /* Up. */ |
case 'A': /* Up. */ |
656 |
debugger_machine->cpus[i], 0, INVALIDATE_ALL); |
debugger_machine->cpus[i], 0, INVALIDATE_ALL); |
657 |
} |
} |
658 |
|
|
659 |
/* |
/* Stop timers while interacting with the user: */ |
660 |
* Ugly GDB hack: After single stepping, we need to send back |
timer_stop(); |
|
* status to GDB: |
|
|
*/ |
|
|
if (exit_debugger == -1) { |
|
|
int i, j; |
|
|
for (i=0; i<debugger_n_emuls; i++) { |
|
|
struct emul *e = debugger_emuls[i]; |
|
|
if (e == NULL) |
|
|
continue; |
|
|
|
|
|
for (j=0; j<e->n_machines; j++) { |
|
|
if (e->machines[j]->gdb.port > 0) |
|
|
debugger_gdb_after_singlestep( |
|
|
e->machines[j]); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
661 |
|
|
662 |
exit_debugger = 0; |
exit_debugger = 0; |
663 |
|
|
665 |
/* Read a line from the terminal: */ |
/* Read a line from the terminal: */ |
666 |
cmd = debugger_readline(); |
cmd = debugger_readline(); |
667 |
|
|
|
/* Special hack for the "step" _GDB_ command: */ |
|
|
if (exit_debugger == -1) |
|
|
return; |
|
|
|
|
668 |
cmd_len = strlen(cmd); |
cmd_len = strlen(cmd); |
669 |
|
|
670 |
/* Remove spaces: */ |
/* Remove spaces: */ |
695 |
return; |
return; |
696 |
} |
} |
697 |
|
|
698 |
|
/* Start up timers again: */ |
699 |
|
timer_start(); |
700 |
|
|
701 |
|
/* ... and reset starttime, so that nr of instructions per second |
702 |
|
can be calculated correctly: */ |
703 |
gettimeofday(&debugger_machine->starttime, NULL); |
gettimeofday(&debugger_machine->starttime, NULL); |
704 |
debugger_machine->ninstrs_since_gettimeofday = 0; |
debugger_machine->ninstrs_since_gettimeofday = 0; |
705 |
|
|
731 |
*/ |
*/ |
732 |
void debugger_init(struct emul **emuls, int n_emuls) |
void debugger_init(struct emul **emuls, int n_emuls) |
733 |
{ |
{ |
734 |
int i, j; |
int i; |
735 |
|
|
736 |
debugger_n_emuls = n_emuls; |
debugger_n_emuls = n_emuls; |
737 |
debugger_emuls = emuls; |
debugger_emuls = emuls; |
748 |
exit(1); |
exit(1); |
749 |
} |
} |
750 |
|
|
|
for (i=0; i<n_emuls; i++) |
|
|
for (j=0; j<emuls[i]->n_machines; j++) |
|
|
debugger_gdb_init(emuls[i]->machines[j]); |
|
|
|
|
751 |
debugger_machine = emuls[0]->machines[0]; |
debugger_machine = emuls[0]->machines[0]; |
752 |
|
|
753 |
|
debugger_cur_cpu = 0; |
754 |
|
debugger_cur_machine = 0; |
755 |
|
debugger_cur_emul = 0; |
756 |
|
|
757 |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
758 |
last_cmd[i] = malloc(MAX_CMD_BUFLEN); |
last_cmd[i] = malloc(MAX_CMD_BUFLEN); |
759 |
if (last_cmd[i] == NULL) { |
if (last_cmd[i] == NULL) { |