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.26 2007/06/15 17:02:39 debug Exp $ |
29 |
* |
* |
30 |
* Single-step debugger. |
* Single-step debugger. |
31 |
* |
* |
32 |
* |
* |
33 |
* TODO: |
* This entire module is very much non-reentrant. :-/ TODO: Fix. |
|
* |
|
|
* This entire module is very much non-reentrant. :-/ |
|
|
* |
|
|
* Add more functionality that already exists elsewhere in the emulator. |
|
|
* |
|
|
* Call stack display (back-trace)? |
|
|
* |
|
|
* 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? |
|
|
* |
|
|
* Nicer looking output of register dumps, floating point registers, |
|
|
* etc. Warn about weird/invalid register contents. |
|
|
* |
|
|
* Ctrl-C doesn't enter the debugger on some OSes (HP-UX?)... |
|
|
* |
|
|
* Many other TODOs. |
|
34 |
*/ |
*/ |
35 |
|
|
36 |
#include <ctype.h> |
#include <ctype.h> |
44 |
#include "cpu.h" |
#include "cpu.h" |
45 |
#include "device.h" |
#include "device.h" |
46 |
#include "debugger.h" |
#include "debugger.h" |
|
#include "debugger_gdb.h" |
|
47 |
#include "diskimage.h" |
#include "diskimage.h" |
48 |
#include "emul.h" |
#include "emul.h" |
49 |
#include "machine.h" |
#include "machine.h" |
51 |
#include "misc.h" |
#include "misc.h" |
52 |
#include "net.h" |
#include "net.h" |
53 |
#include "settings.h" |
#include "settings.h" |
54 |
|
#include "timer.h" |
55 |
#include "x11.h" |
#include "x11.h" |
56 |
|
|
57 |
|
|
87 |
|
|
88 |
static int debugger_n_emuls; |
static int debugger_n_emuls; |
89 |
static struct emul **debugger_emuls; |
static struct emul **debugger_emuls; |
90 |
static struct emul *debugger_emul; |
|
91 |
|
/* Currently focused CPU, machine, and emulation: */ |
92 |
|
int debugger_cur_cpu; |
93 |
|
int debugger_cur_machine; |
94 |
|
int debugger_cur_emul; |
95 |
static struct machine *debugger_machine; |
static struct machine *debugger_machine; |
96 |
|
static struct emul *debugger_emul; |
97 |
|
|
98 |
#define MAX_CMD_BUFLEN 72 |
#define MAX_CMD_BUFLEN 72 |
99 |
#define N_PREVIOUS_CMDS 150 |
#define N_PREVIOUS_CMDS 150 |
113 |
*/ |
*/ |
114 |
char debugger_readchar(void) |
char debugger_readchar(void) |
115 |
{ |
{ |
116 |
int ch, i, j; |
int ch; |
117 |
|
|
118 |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0 && !exit_debugger) { |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0 && !exit_debugger) { |
119 |
/* Check for X11 events: */ |
/* Check for X11 events: */ |
120 |
x11_check_event(debugger_emuls, debugger_n_emuls); |
x11_check_event(debugger_emuls, debugger_n_emuls); |
121 |
|
|
|
/* 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... */ |
|
|
|
|
122 |
/* Give up some CPU time: */ |
/* Give up some CPU time: */ |
123 |
usleep(1); |
usleep(10000); |
124 |
} |
} |
125 |
return ch; |
return ch; |
126 |
} |
} |
164 |
|
|
165 |
|
|
166 |
/* |
/* |
|
* 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; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
167 |
* show_breakpoint(): |
* show_breakpoint(): |
168 |
*/ |
*/ |
169 |
static void show_breakpoint(struct machine *m, int i) |
static void show_breakpoint(struct machine *m, int i) |
170 |
{ |
{ |
171 |
printf("%3i: 0x", i); |
printf("%3i: 0x", i); |
172 |
if (m->cpus[0]->is_32bit) |
if (m->cpus[0]->is_32bit) |
173 |
printf("%08"PRIx32, (uint32_t) m->breakpoint_addr[i]); |
printf("%08"PRIx32, (uint32_t) m->breakpoints.addr[i]); |
174 |
else |
else |
175 |
printf("%016"PRIx64, (uint64_t) m->breakpoint_addr[i]); |
printf("%016"PRIx64, (uint64_t) m->breakpoints.addr[i]); |
176 |
if (m->breakpoint_string[i] != NULL) |
if (m->breakpoints.string[i] != NULL) |
177 |
printf(" (%s)", m->breakpoint_string[i]); |
printf(" (%s)", m->breakpoints.string[i]); |
|
if (m->breakpoint_flags[i]) |
|
|
printf(": flags=0x%x", m->breakpoint_flags[i]); |
|
178 |
printf("\n"); |
printf("\n"); |
179 |
} |
} |
180 |
|
|
200 |
uint64_t tmp; |
uint64_t tmp; |
201 |
uint64_t old_pc = m->cpus[0]->pc; /* TODO: multiple cpus? */ |
uint64_t old_pc = m->cpus[0]->pc; /* TODO: multiple cpus? */ |
202 |
|
|
203 |
left = malloc(MAX_CMD_BUFLEN); |
CHECK_ALLOCATION(left = malloc(MAX_CMD_BUFLEN)); |
|
if (left == NULL) { |
|
|
fprintf(stderr, "out of memory in debugger_assignment()\n"); |
|
|
exit(1); |
|
|
} |
|
204 |
strlcpy(left, cmd, MAX_CMD_BUFLEN); |
strlcpy(left, cmd, MAX_CMD_BUFLEN); |
205 |
right = strchr(left, '='); |
right = strchr(left, '='); |
206 |
if (right == NULL) { |
if (right == NULL) { |
220 |
|
|
221 |
/* printf("left = '%s'\nright = '%s'\n", left, right); */ |
/* printf("left = '%s'\nright = '%s'\n", left, right); */ |
222 |
|
|
223 |
res_right = debugger_parse_name(m, right, 0, &tmp); |
res_right = debugger_parse_expression(m, right, 0, &tmp); |
224 |
switch (res_right) { |
switch (res_right) { |
225 |
case NAME_PARSE_NOMATCH: |
case PARSE_NOMATCH: |
226 |
printf("No match for the right-hand side of the assignment.\n"); |
printf("No match for the right-hand side of the assignment.\n"); |
227 |
break; |
break; |
228 |
case NAME_PARSE_MULTIPLE: |
case PARSE_MULTIPLE: |
229 |
printf("Multiple matches for the right-hand side of the " |
printf("Multiple matches for the right-hand side of the " |
230 |
"assignment.\n"); |
"assignment.\n"); |
231 |
break; |
break; |
232 |
default: |
default: |
233 |
res_left = debugger_parse_name(m, left, 1, &tmp); |
res_left = debugger_parse_expression(m, left, 1, &tmp); |
234 |
switch (res_left) { |
switch (res_left) { |
235 |
case NAME_PARSE_NOMATCH: |
case PARSE_NOMATCH: |
236 |
printf("No match for the left-hand side of the " |
printf("No match for the left-hand side of the " |
237 |
"assignment.\n"); |
"assignment.\n"); |
238 |
break; |
break; |
239 |
case NAME_PARSE_MULTIPLE: |
case PARSE_MULTIPLE: |
240 |
printf("Multiple matches for the left-hand side " |
printf("Multiple matches for the left-hand side " |
241 |
"of the assignment.\n"); |
"of the assignment.\n"); |
242 |
break; |
break; |
549 |
} else if (ch == 27) { |
} else if (ch == 27) { |
550 |
/* Escape codes: (cursor keys etc) */ |
/* Escape codes: (cursor keys etc) */ |
551 |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) |
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) |
552 |
usleep(1); |
usleep(10000); |
553 |
if (ch == '[' || ch == 'O') { |
if (ch == '[' || ch == 'O') { |
554 |
while ((ch = console_readchar(MAIN_CONSOLE)) |
while ((ch = console_readchar(MAIN_CONSOLE)) |
555 |
< 0) |
< 0) |
556 |
usleep(1); |
usleep(10000); |
557 |
switch (ch) { |
switch (ch) { |
558 |
case '2': /* 2~ = ins */ |
case '2': /* 2~ = ins */ |
559 |
case '5': /* 5~ = pgup */ |
case '5': /* 5~ = pgup */ |
561 |
/* TODO: Ugly hack, but might work. */ |
/* TODO: Ugly hack, but might work. */ |
562 |
while ((ch = console_readchar( |
while ((ch = console_readchar( |
563 |
MAIN_CONSOLE)) < 0) |
MAIN_CONSOLE)) < 0) |
564 |
usleep(1); |
usleep(10000); |
565 |
/* Do nothing for these keys. */ |
/* Do nothing for these keys. */ |
566 |
break; |
break; |
567 |
case '3': /* 3~ = delete */ |
case '3': /* 3~ = delete */ |
568 |
/* TODO: Ugly hack, but might work. */ |
/* TODO: Ugly hack, but might work. */ |
569 |
while ((ch = console_readchar( |
while ((ch = console_readchar( |
570 |
MAIN_CONSOLE)) < 0) |
MAIN_CONSOLE)) < 0) |
571 |
usleep(1); |
usleep(10000); |
572 |
console_makeavail(MAIN_CONSOLE, '\b'); |
console_makeavail(MAIN_CONSOLE, '\b'); |
573 |
break; |
break; |
574 |
case 'A': /* Up. */ |
case 'A': /* Up. */ |
637 |
debugger_machine->cpus[i], 0, INVALIDATE_ALL); |
debugger_machine->cpus[i], 0, INVALIDATE_ALL); |
638 |
} |
} |
639 |
|
|
640 |
/* |
/* Stop timers while interacting with the user: */ |
641 |
* 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]); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
642 |
|
|
643 |
exit_debugger = 0; |
exit_debugger = 0; |
644 |
|
|
646 |
/* Read a line from the terminal: */ |
/* Read a line from the terminal: */ |
647 |
cmd = debugger_readline(); |
cmd = debugger_readline(); |
648 |
|
|
|
/* Special hack for the "step" _GDB_ command: */ |
|
|
if (exit_debugger == -1) |
|
|
return; |
|
|
|
|
649 |
cmd_len = strlen(cmd); |
cmd_len = strlen(cmd); |
650 |
|
|
651 |
/* Remove spaces: */ |
/* Remove spaces: */ |
676 |
return; |
return; |
677 |
} |
} |
678 |
|
|
679 |
gettimeofday(&debugger_machine->starttime, NULL); |
/* Start up timers again: */ |
680 |
debugger_machine->ninstrs_since_gettimeofday = 0; |
timer_start(); |
681 |
|
|
682 |
|
/* ... and reset starttime, so that nr of instructions per second |
683 |
|
can be calculated correctly: */ |
684 |
|
for (i=0; i<debugger_machine->ncpus; i++) { |
685 |
|
gettimeofday(&debugger_machine->cpus[i]->starttime, NULL); |
686 |
|
debugger_machine->cpus[i]->ninstrs_since_gettimeofday = 0; |
687 |
|
} |
688 |
|
|
689 |
single_step = NOT_SINGLE_STEPPING; |
single_step = NOT_SINGLE_STEPPING; |
690 |
debugger_machine->instruction_trace = old_instruction_trace; |
debugger_machine->instruction_trace = old_instruction_trace; |
714 |
*/ |
*/ |
715 |
void debugger_init(struct emul **emuls, int n_emuls) |
void debugger_init(struct emul **emuls, int n_emuls) |
716 |
{ |
{ |
717 |
int i, j; |
int i; |
718 |
|
|
719 |
debugger_n_emuls = n_emuls; |
debugger_n_emuls = n_emuls; |
720 |
debugger_emuls = emuls; |
debugger_emuls = emuls; |
731 |
exit(1); |
exit(1); |
732 |
} |
} |
733 |
|
|
|
for (i=0; i<n_emuls; i++) |
|
|
for (j=0; j<emuls[i]->n_machines; j++) |
|
|
debugger_gdb_init(emuls[i]->machines[j]); |
|
|
|
|
734 |
debugger_machine = emuls[0]->machines[0]; |
debugger_machine = emuls[0]->machines[0]; |
735 |
|
|
736 |
|
debugger_cur_cpu = 0; |
737 |
|
debugger_cur_machine = 0; |
738 |
|
debugger_cur_emul = 0; |
739 |
|
|
740 |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
741 |
last_cmd[i] = malloc(MAX_CMD_BUFLEN); |
CHECK_ALLOCATION(last_cmd[i] = malloc(MAX_CMD_BUFLEN)); |
|
if (last_cmd[i] == NULL) { |
|
|
fprintf(stderr, "debugger_init(): out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
742 |
last_cmd[i][0] = '\0'; |
last_cmd[i][0] = '\0'; |
743 |
} |
} |
744 |
|
|