25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: debugger.c,v 1.103 2005/05/13 14:26:29 debug Exp $ |
* $Id: debugger.c,v 1.123 2005/10/26 14:37:01 debug Exp $ |
29 |
* |
* |
30 |
* Single-step debugger. |
* Single-step debugger. |
31 |
* |
* |
82 |
int force_debugger_at_exit = 0; |
int force_debugger_at_exit = 0; |
83 |
int show_opcode_statistics = 0; |
int show_opcode_statistics = 0; |
84 |
|
|
85 |
|
volatile int single_step_breakpoint = 0; |
86 |
|
int debugger_n_steps_left_before_interaction = 0; |
87 |
|
|
88 |
int old_instruction_trace = 0; |
int old_instruction_trace = 0; |
89 |
int old_quiet_mode = 0; |
int old_quiet_mode = 0; |
90 |
int old_show_trace_tree = 0; |
int old_show_trace_tree = 0; |
102 |
static struct machine *debugger_machine; |
static struct machine *debugger_machine; |
103 |
|
|
104 |
static int exit_debugger; |
static int exit_debugger; |
|
static int n_steps_left_before_interaction = 0; |
|
105 |
|
|
106 |
#define MAX_CMD_LEN 70 |
#define MAX_CMD_BUFLEN 72 |
107 |
#define N_PREVIOUS_CMDS 150 |
#define N_PREVIOUS_CMDS 150 |
108 |
static char *last_cmd[N_PREVIOUS_CMDS]; |
static char *last_cmd[N_PREVIOUS_CMDS]; |
109 |
static int last_cmd_index; |
static int last_cmd_index; |
110 |
|
|
111 |
static char repeat_cmd[MAX_CMD_LEN + 1]; |
static char repeat_cmd[MAX_CMD_BUFLEN]; |
112 |
|
|
113 |
#define MAGIC_UNTOUCHED 0x98ca76c2ffcc0011ULL |
#define MAGIC_UNTOUCHED 0x98ca76c2ffcc0011ULL |
114 |
|
|
117 |
|
|
118 |
|
|
119 |
/* |
/* |
120 |
|
* debugger_readchar(): |
121 |
|
* |
122 |
|
* TODO: This uses up 100% CPU, maybe that isn't too good. The usleep() call |
123 |
|
* might make it a tiny bit nicer on other running processes, but it |
124 |
|
* is still very ugly. |
125 |
|
*/ |
126 |
|
char debugger_readchar(void) |
127 |
|
{ |
128 |
|
int ch; |
129 |
|
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) { |
130 |
|
x11_check_event(debugger_emuls, debugger_n_emuls); |
131 |
|
usleep(1); |
132 |
|
} |
133 |
|
return ch; |
134 |
|
} |
135 |
|
|
136 |
|
|
137 |
|
/* |
138 |
* debugger_activate(): |
* debugger_activate(): |
139 |
* |
* |
140 |
* This is a signal handler for CTRL-C. It shouldn't be called directly, |
* This is a signal handler for CTRL-C. It shouldn't be called directly, |
148 |
if (single_step) { |
if (single_step) { |
149 |
/* Already in the debugger. Do nothing. */ |
/* Already in the debugger. Do nothing. */ |
150 |
int i; |
int i; |
151 |
for (i=0; i<MAX_CMD_LEN+1; i++) |
for (i=0; i<MAX_CMD_BUFLEN; i++) |
152 |
console_makeavail(MAIN_CONSOLE, '\b'); |
console_makeavail(MAIN_CONSOLE, '\b'); |
153 |
console_makeavail(MAIN_CONSOLE, ' '); |
console_makeavail(MAIN_CONSOLE, ' '); |
154 |
console_makeavail(MAIN_CONSOLE, '\n'); |
console_makeavail(MAIN_CONSOLE, '\n'); |
180 |
* Some examples: |
* Some examples: |
181 |
* |
* |
182 |
* "0x7fff1234" ==> numeric value (hex, in this case) |
* "0x7fff1234" ==> numeric value (hex, in this case) |
183 |
* "pc", "r5", "hi", "t4" ==> register (CPU dependant) |
* "pc", "r5", "hi", "t4" ==> register (CPU dependent) |
184 |
* "memcpy+64" ==> symbol (plus offset) |
* "memcpy+64" ==> symbol (plus offset) |
185 |
* |
* |
186 |
* Register names can be preceeded by "x:" where x is the CPU number. (CPU |
* Register names can be preceeded by "x:" where x is the CPU number. (CPU |
218 |
} |
} |
219 |
|
|
220 |
/* Warn about non-signextended values: */ |
/* Warn about non-signextended values: */ |
221 |
if (writeflag && |
if (writeflag) { |
222 |
((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL) |
if (m->cpus[0]->is_32bit) { |
223 |
printf("WARNING: The value is not sign-extended. " |
/* Automagically sign-extend. TODO: Is this good? */ |
224 |
"Is this what you intended?\n"); |
if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL) |
225 |
|
(*valuep) |= 0xffffffff00000000ULL; |
226 |
|
} else { |
227 |
|
if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL) |
228 |
|
printf("WARNING: The value is not sign-extende" |
229 |
|
"d. Is this what you intended?\n"); |
230 |
|
} |
231 |
|
} |
232 |
|
|
233 |
skip_register = name[0] == '$' || name[0] == '@'; |
skip_register = name[0] == '$' || name[0] == '@'; |
234 |
skip_numeric = name[0] == '%' || name[0] == '@'; |
skip_numeric = name[0] == '%' || name[0] == '@'; |
261 |
fprintf(stderr, "out of memory in debugger\n"); |
fprintf(stderr, "out of memory in debugger\n"); |
262 |
exit(1); |
exit(1); |
263 |
} |
} |
264 |
strcpy(sn, name); |
strlcpy(sn, name, strlen(name)+1); |
265 |
|
|
266 |
/* Is there a '+' in there? Then treat that as an offset: */ |
/* Is there a '+' in there? Then treat that as an offset: */ |
267 |
p = strchr(sn, '+'); |
p = strchr(sn, '+'); |
301 |
*/ |
*/ |
302 |
static void show_breakpoint(struct machine *m, int i) |
static void show_breakpoint(struct machine *m, int i) |
303 |
{ |
{ |
304 |
printf("%3i: 0x%016llx", i, |
printf("%3i: 0x", i); |
305 |
(long long)m->breakpoint_addr[i]); |
if (m->cpus[0]->is_32bit) |
306 |
|
printf("%08x", (int)m->breakpoint_addr[i]); |
307 |
|
else |
308 |
|
printf("%016llx", (long long)m->breakpoint_addr[i]); |
309 |
if (m->breakpoint_string[i] != NULL) |
if (m->breakpoint_string[i] != NULL) |
310 |
printf(" (%s)", m->breakpoint_string[i]); |
printf(" (%s)", m->breakpoint_string[i]); |
311 |
if (m->breakpoint_flags[i]) |
if (m->breakpoint_flags[i]) |
367 |
m->breakpoint_flags[i] = m->breakpoint_flags[i+1]; |
m->breakpoint_flags[i] = m->breakpoint_flags[i+1]; |
368 |
} |
} |
369 |
m->n_breakpoints --; |
m->n_breakpoints --; |
370 |
|
|
371 |
|
/* Clear translations: */ |
372 |
|
for (i=0; i<m->ncpus; i++) |
373 |
|
if (m->cpus[i]->translation_cache != NULL) |
374 |
|
cpu_create_or_reset_tc(m->cpus[i]); |
375 |
return; |
return; |
376 |
} |
} |
377 |
|
|
378 |
if (strncmp(cmd_line, "add ", 4) == 0) { |
if (strncmp(cmd_line, "add ", 4) == 0) { |
379 |
uint64_t tmp; |
uint64_t tmp; |
380 |
|
size_t breakpoint_buf_len; |
381 |
|
|
382 |
if (m->n_breakpoints >= MAX_BREAKPOINTS) { |
if (m->n_breakpoints >= MAX_BREAKPOINTS) { |
383 |
printf("Too many breakpoints. (You need to recompile" |
printf("Too many breakpoints. (You need to recompile" |
394 |
return; |
return; |
395 |
} |
} |
396 |
|
|
397 |
m->breakpoint_string[i] = malloc(strlen(cmd_line+4) + 1); |
breakpoint_buf_len = strlen(cmd_line+4) + 1; |
398 |
|
m->breakpoint_string[i] = malloc(breakpoint_buf_len); |
399 |
if (m->breakpoint_string[i] == NULL) { |
if (m->breakpoint_string[i] == NULL) { |
400 |
printf("out of memory in debugger_cmd_breakpoint()\n"); |
printf("out of memory in debugger_cmd_breakpoint()\n"); |
401 |
exit(1); |
exit(1); |
402 |
} |
} |
403 |
strcpy(m->breakpoint_string[i], cmd_line+4); |
strlcpy(m->breakpoint_string[i], cmd_line+4, |
404 |
|
breakpoint_buf_len); |
405 |
m->breakpoint_addr[i] = tmp; |
m->breakpoint_addr[i] = tmp; |
406 |
m->breakpoint_flags[i] = 0; |
m->breakpoint_flags[i] = 0; |
407 |
|
|
408 |
m->n_breakpoints ++; |
m->n_breakpoints ++; |
409 |
show_breakpoint(m, i); |
show_breakpoint(m, i); |
410 |
|
|
411 |
|
/* Clear translations: */ |
412 |
|
for (i=0; i<m->ncpus; i++) |
413 |
|
if (m->cpus[i]->translation_cache != NULL) |
414 |
|
cpu_create_or_reset_tc(m->cpus[i]); |
415 |
return; |
return; |
416 |
} |
} |
417 |
|
|
556 |
(long long)mem->dev_length[i]); |
(long long)mem->dev_length[i]); |
557 |
if (mem->dev_flags[i]) { |
if (mem->dev_flags[i]) { |
558 |
printf(" ("); |
printf(" ("); |
559 |
if (mem->dev_flags[i] & MEM_BINTRANS_OK) |
if (mem->dev_flags[i] & MEM_DYNTRANS_OK) |
560 |
printf("BINTRANS R"); |
printf("DYNTRANS R"); |
561 |
if (mem->dev_flags[i] & MEM_BINTRANS_WRITE_OK) |
if (mem->dev_flags[i] & MEM_DYNTRANS_WRITE_OK) |
562 |
printf("+W"); |
printf("+W"); |
563 |
printf(")"); |
printf(")"); |
564 |
} |
} |
668 |
r = c->memory_rw(c, mem, addr, &buf[0], sizeof(buf), |
r = c->memory_rw(c, mem, addr, &buf[0], sizeof(buf), |
669 |
MEM_READ, CACHE_NONE | NO_EXCEPTIONS); |
MEM_READ, CACHE_NONE | NO_EXCEPTIONS); |
670 |
|
|
671 |
printf("0x%016llx ", (long long)addr); |
if (c->is_32bit) |
672 |
|
printf("0x%08x ", (int)addr); |
673 |
|
else |
674 |
|
printf("0x%016llx ", (long long)addr); |
675 |
|
|
676 |
if (r == MEMORY_ACCESS_FAILED) |
if (r == MEMORY_ACCESS_FAILED) |
677 |
printf("(memory access failed)\n"); |
printf("(memory access failed)\n"); |
704 |
|
|
705 |
last_dump_addr = addr_end; |
last_dump_addr = addr_end; |
706 |
|
|
707 |
strcpy(repeat_cmd, "dump"); |
strlcpy(repeat_cmd, "dump", MAX_CMD_BUFLEN); |
708 |
} |
} |
709 |
|
|
710 |
|
|
846 |
printf("lookup for '%s' failed\n", cmd_line); |
printf("lookup for '%s' failed\n", cmd_line); |
847 |
return; |
return; |
848 |
} |
} |
849 |
printf("%s = 0x%016llx\n", cmd_line, (long long)newaddr); |
printf("%s = 0x", cmd_line); |
850 |
|
if (m->cpus[0]->is_32bit) |
851 |
|
printf("%08x\n", (int)newaddr); |
852 |
|
else |
853 |
|
printf("%016llx\n", (long long)newaddr); |
854 |
return; |
return; |
855 |
} |
} |
856 |
|
|
857 |
symbol = get_symbol_name(&m->symbol_context, addr, &offset); |
symbol = get_symbol_name(&m->symbol_context, addr, &offset); |
858 |
|
|
859 |
if (symbol != NULL) |
if (symbol != NULL) { |
860 |
printf("0x%016llx = %s\n", (long long)addr, symbol); |
if (m->cpus[0]->is_32bit) |
861 |
else |
printf("0x%08x", (int)addr); |
862 |
|
else |
863 |
|
printf("0x%016llx", (long long)addr); |
864 |
|
printf(" = %s\n", symbol); |
865 |
|
} else |
866 |
printf("lookup for '%s' failed\n", cmd_line); |
printf("lookup for '%s' failed\n", cmd_line); |
867 |
} |
} |
868 |
|
|
889 |
|
|
890 |
|
|
891 |
/* |
/* |
892 |
|
* debugger_cmd_ninstrs(): |
893 |
|
*/ |
894 |
|
static void debugger_cmd_ninstrs(struct machine *m, char *cmd_line) |
895 |
|
{ |
896 |
|
int toggle = 1; |
897 |
|
int previous_mode = m->show_nr_of_instructions; |
898 |
|
|
899 |
|
if (cmd_line[0] != '\0') { |
900 |
|
while (cmd_line[0] != '\0' && cmd_line[0] == ' ') |
901 |
|
cmd_line ++; |
902 |
|
switch (cmd_line[0]) { |
903 |
|
case '0': |
904 |
|
toggle = 0; |
905 |
|
m->show_nr_of_instructions = 0; |
906 |
|
break; |
907 |
|
case '1': |
908 |
|
toggle = 0; |
909 |
|
m->show_nr_of_instructions = 1; |
910 |
|
break; |
911 |
|
case 'o': |
912 |
|
case 'O': |
913 |
|
toggle = 0; |
914 |
|
switch (cmd_line[1]) { |
915 |
|
case 'n': |
916 |
|
case 'N': |
917 |
|
m->show_nr_of_instructions = 1; |
918 |
|
break; |
919 |
|
default: |
920 |
|
m->show_nr_of_instructions = 0; |
921 |
|
} |
922 |
|
break; |
923 |
|
default: |
924 |
|
printf("syntax: trace [on|off]\n"); |
925 |
|
return; |
926 |
|
} |
927 |
|
} |
928 |
|
|
929 |
|
if (toggle) |
930 |
|
m->show_nr_of_instructions = !m->show_nr_of_instructions; |
931 |
|
|
932 |
|
printf("show_nr_of_instructions = %s", |
933 |
|
m->show_nr_of_instructions? "ON" : "OFF"); |
934 |
|
if (m->show_nr_of_instructions != previous_mode) |
935 |
|
printf(" (was: %s)", previous_mode? "ON" : "OFF"); |
936 |
|
printf("\n"); |
937 |
|
} |
938 |
|
|
939 |
|
|
940 |
|
/* |
941 |
* debugger_cmd_opcodestats(): |
* debugger_cmd_opcodestats(): |
942 |
*/ |
*/ |
943 |
static void debugger_cmd_opcodestats(struct machine *m, char *cmd_line) |
static void debugger_cmd_opcodestats(struct machine *m, char *cmd_line) |
1010 |
printf("%s = 0x%llx\n", cmd_line, (long long)tmp); |
printf("%s = 0x%llx\n", cmd_line, (long long)tmp); |
1011 |
break; |
break; |
1012 |
case NAME_PARSE_SYMBOL: |
case NAME_PARSE_SYMBOL: |
1013 |
printf("%s = 0x%016llx\n", cmd_line, (long long)tmp); |
if (m->cpus[0]->is_32bit) |
1014 |
|
printf("%s = 0x%08x\n", cmd_line, (int)tmp); |
1015 |
|
else |
1016 |
|
printf("%s = 0x%016llx\n", cmd_line, (long long)tmp); |
1017 |
break; |
break; |
1018 |
case NAME_PARSE_NUMBER: |
case NAME_PARSE_NUMBER: |
1019 |
printf("0x%llx\n", (long long)tmp); |
printf("0x%llx\n", (long long)tmp); |
1125 |
switch (put_type) { |
switch (put_type) { |
1126 |
case 'b': |
case 'b': |
1127 |
a_byte = data; |
a_byte = data; |
1128 |
printf("0x%016llx: %02x", (long long)addr, a_byte); |
if (m->cpus[0]->is_32bit) |
1129 |
|
printf("0x%08x", (int)addr); |
1130 |
|
else |
1131 |
|
printf("0x%016llx", (long long)addr); |
1132 |
|
printf(": %02x", a_byte); |
1133 |
if (data > 255) |
if (data > 255) |
1134 |
printf(" (NOTE: truncating %0llx)", (long long)data); |
printf(" (NOTE: truncating %0llx)", (long long)data); |
1135 |
res = m->cpus[0]->memory_rw(m->cpus[0], m->cpus[0]->mem, addr, |
res = m->cpus[0]->memory_rw(m->cpus[0], m->cpus[0]->mem, addr, |
1141 |
case 'h': |
case 'h': |
1142 |
if ((data & 1) != 0) |
if ((data & 1) != 0) |
1143 |
printf("WARNING: address isn't aligned\n"); |
printf("WARNING: address isn't aligned\n"); |
1144 |
printf("0x%016llx: %04x", (long long)addr, (int)data); |
if (m->cpus[0]->is_32bit) |
1145 |
|
printf("0x%08x", (int)addr); |
1146 |
|
else |
1147 |
|
printf("0x%016llx", (long long)addr); |
1148 |
|
printf(": %04x", (int)data); |
1149 |
if (data > 0xffff) |
if (data > 0xffff) |
1150 |
printf(" (NOTE: truncating %0llx)", (long long)data); |
printf(" (NOTE: truncating %0llx)", (long long)data); |
1151 |
res = store_16bit_word(m->cpus[0], addr, data); |
res = store_16bit_word(m->cpus[0], addr, data); |
1156 |
case 'w': |
case 'w': |
1157 |
if ((data & 3) != 0) |
if ((data & 3) != 0) |
1158 |
printf("WARNING: address isn't aligned\n"); |
printf("WARNING: address isn't aligned\n"); |
1159 |
printf("0x%016llx: %08x", (long long)addr, (int)data); |
if (m->cpus[0]->is_32bit) |
1160 |
|
printf("0x%08x", (int)addr); |
1161 |
|
else |
1162 |
|
printf("0x%016llx", (long long)addr); |
1163 |
|
printf(": %08x", (int)data); |
1164 |
if (data > 0xffffffff && (data >> 32) != 0 |
if (data > 0xffffffff && (data >> 32) != 0 |
1165 |
&& (data >> 32) != 0xffffffff) |
&& (data >> 32) != 0xffffffff) |
1166 |
printf(" (NOTE: truncating %0llx)", (long long)data); |
printf(" (NOTE: truncating %0llx)", (long long)data); |
1172 |
case 'd': |
case 'd': |
1173 |
if ((data & 7) != 0) |
if ((data & 7) != 0) |
1174 |
printf("WARNING: address isn't aligned\n"); |
printf("WARNING: address isn't aligned\n"); |
1175 |
printf("0x%016llx: %016llx", (long long)addr, (long long)data); |
if (m->cpus[0]->is_32bit) |
1176 |
|
printf("0x%08x", (int)addr); |
1177 |
|
else |
1178 |
|
printf("0x%016llx", (long long)addr); |
1179 |
|
printf(": %016llx", (long long)data); |
1180 |
res = store_64bit_word(m->cpus[0], addr, data); |
res = store_64bit_word(m->cpus[0], addr, data); |
1181 |
if (!res) |
if (!res) |
1182 |
printf(" FAILED!\n"); |
printf(" FAILED!\n"); |
1326 |
} |
} |
1327 |
} |
} |
1328 |
|
|
1329 |
n_steps_left_before_interaction = n - 1; |
debugger_n_steps_left_before_interaction = n - 1; |
1330 |
|
|
1331 |
/* Special hack, see debugger() for more info. */ |
/* Special hack, see debugger() for more info. */ |
1332 |
exit_debugger = -1; |
exit_debugger = -1; |
1333 |
|
|
1334 |
strcpy(repeat_cmd, "step"); |
strlcpy(repeat_cmd, "step", MAX_CMD_BUFLEN); |
1335 |
} |
} |
1336 |
|
|
1337 |
|
|
1378 |
*/ |
*/ |
1379 |
static void debugger_cmd_trace(struct machine *m, char *cmd_line) |
static void debugger_cmd_trace(struct machine *m, char *cmd_line) |
1380 |
{ |
{ |
1381 |
if (*cmd_line) { |
int i, toggle = 1; |
1382 |
printf("syntax: trace\n"); |
int previous_mode = old_show_trace_tree; |
1383 |
return; |
|
1384 |
|
if (cmd_line[0] != '\0') { |
1385 |
|
while (cmd_line[0] != '\0' && cmd_line[0] == ' ') |
1386 |
|
cmd_line ++; |
1387 |
|
switch (cmd_line[0]) { |
1388 |
|
case '0': |
1389 |
|
toggle = 0; |
1390 |
|
old_show_trace_tree = 0; |
1391 |
|
break; |
1392 |
|
case '1': |
1393 |
|
toggle = 0; |
1394 |
|
old_show_trace_tree = 1; |
1395 |
|
break; |
1396 |
|
case 'o': |
1397 |
|
case 'O': |
1398 |
|
toggle = 0; |
1399 |
|
switch (cmd_line[1]) { |
1400 |
|
case 'n': |
1401 |
|
case 'N': |
1402 |
|
old_show_trace_tree = 1; |
1403 |
|
break; |
1404 |
|
default: |
1405 |
|
old_show_trace_tree = 0; |
1406 |
|
} |
1407 |
|
break; |
1408 |
|
default: |
1409 |
|
printf("syntax: trace [on|off]\n"); |
1410 |
|
return; |
1411 |
|
} |
1412 |
} |
} |
1413 |
|
|
1414 |
old_show_trace_tree = 1 - old_show_trace_tree; |
if (toggle) |
1415 |
printf("show_trace_tree = %s\n", old_show_trace_tree? "ON" : "OFF"); |
old_show_trace_tree = 1 - old_show_trace_tree; |
1416 |
|
|
1417 |
|
printf("show_trace_tree = %s", old_show_trace_tree? "ON" : "OFF"); |
1418 |
|
if (old_show_trace_tree != previous_mode) |
1419 |
|
printf(" (was: %s)", previous_mode? "ON" : "OFF"); |
1420 |
|
printf("\n"); |
1421 |
|
|
1422 |
if (m->bintrans_enable && old_show_trace_tree) |
if (m->bintrans_enable && old_show_trace_tree) |
1423 |
printf("NOTE: the trace tree functionality doesn't " |
printf("NOTE: the trace tree functionality doesn't " |
1424 |
"work very well with bintrans!\n"); |
"work very well with bintrans!\n"); |
1425 |
|
|
1426 |
/* TODO: how to preserve quiet_mode? */ |
/* Clear translations: */ |
1427 |
old_quiet_mode = 0; |
for (i=0; i<m->ncpus; i++) |
1428 |
printf("quiet_mode = %s\n", old_quiet_mode? "ON" : "OFF"); |
if (m->cpus[i]->translation_cache != NULL) |
1429 |
|
cpu_create_or_reset_tc(m->cpus[i]); |
1430 |
} |
} |
1431 |
|
|
1432 |
|
|
1510 |
ctrl_c = 0; |
ctrl_c = 0; |
1511 |
|
|
1512 |
while (addr < addr_end) { |
while (addr < addr_end) { |
1513 |
int i, len; |
unsigned int i, len; |
1514 |
unsigned char buf[32]; /* TODO: How long can an |
unsigned char buf[17]; /* TODO: How long can an |
1515 |
instruction be, on weird archs? */ |
instruction be, on weird archs? */ |
1516 |
memset(buf, 0, sizeof(buf)); |
memset(buf, 0, sizeof(buf)); |
1517 |
|
|
1537 |
|
|
1538 |
last_unasm_addr = addr; |
last_unasm_addr = addr; |
1539 |
|
|
1540 |
strcpy(repeat_cmd, "unassemble"); |
strlcpy(repeat_cmd, "unassemble", MAX_CMD_BUFLEN); |
1541 |
} |
} |
1542 |
|
|
1543 |
|
|
1604 |
{ "machine", "", 0, debugger_cmd_machine, |
{ "machine", "", 0, debugger_cmd_machine, |
1605 |
"print a summary of the current machine" }, |
"print a summary of the current machine" }, |
1606 |
|
|
1607 |
|
{ "ninstrs", "[on|off]", 0, debugger_cmd_ninstrs, |
1608 |
|
"toggle (set or unset) show_nr_of_instructions" }, |
1609 |
|
|
1610 |
{ "opcodestats", "", 0, debugger_cmd_opcodestats, |
{ "opcodestats", "", 0, debugger_cmd_opcodestats, |
1611 |
"show opcode statistics" }, |
"show opcode statistics" }, |
1612 |
|
|
1637 |
{ "tlbdump", "[cpuid][,r]", 0, debugger_cmd_tlbdump, |
{ "tlbdump", "[cpuid][,r]", 0, debugger_cmd_tlbdump, |
1638 |
"dump TLB contents (add ',r' for raw data)" }, |
"dump TLB contents (add ',r' for raw data)" }, |
1639 |
|
|
1640 |
{ "trace", "", 0, debugger_cmd_trace, |
{ "trace", "[on|off]", 0, debugger_cmd_trace, |
1641 |
"toggle show_trace_tree on or off" }, |
"toggle show_trace_tree on or off" }, |
1642 |
|
|
1643 |
{ "unassemble", "[addr [endaddr]]", 0, debugger_cmd_unassemble, |
{ "unassemble", "[addr [endaddr]]", 0, debugger_cmd_unassemble, |
1646 |
{ "version", "", 0, debugger_cmd_version, |
{ "version", "", 0, debugger_cmd_version, |
1647 |
"print version information" }, |
"print version information" }, |
1648 |
|
|
1649 |
|
/* Note: NULL handler. */ |
1650 |
|
{ "x = expr", "", 0, NULL, "generic assignment" }, |
1651 |
|
|
1652 |
{ NULL, NULL, 0, NULL, NULL } |
{ NULL, NULL, 0, NULL, NULL } |
1653 |
}; |
}; |
1654 |
|
|
1660 |
* |
* |
1661 |
* NOTE: This is placed after the cmds[] array, because it needs to |
* NOTE: This is placed after the cmds[] array, because it needs to |
1662 |
* access it. |
* access it. |
1663 |
|
* |
1664 |
|
* TODO: Command completion (ie just type "help s" for "help step"). |
1665 |
*/ |
*/ |
1666 |
static void debugger_cmd_help(struct machine *m, char *cmd_line) |
static void debugger_cmd_help(struct machine *m, char *cmd_line) |
1667 |
{ |
{ |
1668 |
int i, j, max_name_len = 0; |
int i, max_name_len = 0, only_one = 0, only_one_match = 0; |
1669 |
|
char *nlines_env = getenv("LINES"); |
1670 |
|
int nlines = atoi(nlines_env != NULL? nlines_env : "999999"); |
1671 |
|
int j, curlines; |
1672 |
|
|
1673 |
|
if (cmd_line[0] != '\0') { |
1674 |
|
only_one = 1; |
1675 |
|
} |
1676 |
|
|
1677 |
i = 0; |
i = 0; |
1678 |
while (cmds[i].name != NULL) { |
while (cmds[i].name != NULL) { |
1684 |
i++; |
i++; |
1685 |
} |
} |
1686 |
|
|
1687 |
printf("Available commands:\n"); |
curlines = 0; |
1688 |
|
if (!only_one) { |
1689 |
|
printf("Available commands:\n"); |
1690 |
|
curlines++; |
1691 |
|
} |
1692 |
|
|
1693 |
i = 0; |
i = 0; |
1694 |
while (cmds[i].name != NULL) { |
while (cmds[i].name != NULL) { |
1695 |
char buf[100]; |
char buf[100]; |
1696 |
snprintf(buf, sizeof(buf), "%s", cmds[i].name); |
snprintf(buf, sizeof(buf), "%s", cmds[i].name); |
1697 |
|
|
1698 |
|
if (only_one) { |
1699 |
|
if (strcmp(cmds[i].name, cmd_line) != 0) { |
1700 |
|
i++; |
1701 |
|
continue; |
1702 |
|
} |
1703 |
|
only_one_match = 1; |
1704 |
|
} |
1705 |
|
|
1706 |
if (cmds[i].args != NULL) |
if (cmds[i].args != NULL) |
1707 |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1708 |
" %s", cmds[i].args); |
" %s", cmds[i].args); |
1716 |
|
|
1717 |
printf(" %s\n", cmds[i].description); |
printf(" %s\n", cmds[i].description); |
1718 |
i++; |
i++; |
1719 |
|
|
1720 |
|
curlines ++; |
1721 |
|
if (curlines >= nlines - 1) { |
1722 |
|
char ch; |
1723 |
|
printf("-- more --"); fflush(stdout); |
1724 |
|
ch = debugger_readchar(); |
1725 |
|
printf("\n"); |
1726 |
|
if (ch == 'q' || ch == 'Q') |
1727 |
|
return; |
1728 |
|
curlines = 0; |
1729 |
|
} |
1730 |
|
} |
1731 |
|
|
1732 |
|
if (only_one) { |
1733 |
|
if (!only_one_match) |
1734 |
|
printf("%s: no such command\n", cmd_line); |
1735 |
|
return; |
1736 |
|
} |
1737 |
|
|
1738 |
|
/* TODO: generalize/refactor */ |
1739 |
|
curlines += 8; |
1740 |
|
if (curlines > nlines - 1) { |
1741 |
|
char ch; |
1742 |
|
printf("-- more --"); fflush(stdout); |
1743 |
|
ch = debugger_readchar(); |
1744 |
|
printf("\n"); |
1745 |
|
if (ch == 'q' || ch == 'Q') |
1746 |
|
return; |
1747 |
|
curlines = 0; |
1748 |
} |
} |
1749 |
|
|
1750 |
printf("Generic assignments: x = expr\n"); |
printf("\nIn generic assignments, x must be a register, and expr can be" |
1751 |
printf("where x must be a register, and expr can be a register, a " |
" a register, a\nnumeric value, or a symbol name (+ an optional " |
1752 |
"numeric value, or\na symbol name (+ an optional numeric offset)." |
"numeric offset). In case there\nare multiple matches (i.e. a " |
1753 |
" In case there are multiple\nmatches (ie a symbol that has the " |
"symbol that has the same name as a register), you\nmay add a " |
1754 |
"same name as a register), you may add a\nprefix character as a " |
"prefix character as a hint: '%%' for registers, '@' for symbols," |
1755 |
"hint: '%%' for registers, '@' for symbols, and\n'$' for numeric" |
" and\n'$' for numeric values. Use 0x for hexadecimal values.\n"); |
|
" values. Use 0x for hexadecimal values.\n"); |
|
1756 |
} |
} |
1757 |
|
|
1758 |
|
|
1769 |
char *left, *right; |
char *left, *right; |
1770 |
int res_left, res_right; |
int res_left, res_right; |
1771 |
uint64_t tmp; |
uint64_t tmp; |
1772 |
|
uint64_t old_pc = m->cpus[0]->pc; /* TODO: multiple cpus? */ |
1773 |
|
|
1774 |
left = malloc(strlen(cmd) + 1); |
left = malloc(MAX_CMD_BUFLEN); |
1775 |
if (left == NULL) { |
if (left == NULL) { |
1776 |
fprintf(stderr, "out of memory in debugger_assignment()\n"); |
fprintf(stderr, "out of memory in debugger_assignment()\n"); |
1777 |
exit(1); |
exit(1); |
1778 |
} |
} |
1779 |
strcpy(left, cmd); |
strlcpy(left, cmd, MAX_CMD_BUFLEN); |
1780 |
right = strchr(left, '='); |
right = strchr(left, '='); |
1781 |
if (right == NULL) { |
if (right == NULL) { |
1782 |
fprintf(stderr, "internal error in the debugger\n"); |
fprintf(stderr, "internal error in the debugger\n"); |
1820 |
} |
} |
1821 |
} |
} |
1822 |
|
|
1823 |
|
/* |
1824 |
|
* If the PC has changed, then release any breakpoint we were |
1825 |
|
* currently stopped at. |
1826 |
|
* |
1827 |
|
* TODO: multiple cpus? |
1828 |
|
*/ |
1829 |
|
if (old_pc != m->cpus[0]->pc) |
1830 |
|
single_step_breakpoint = 0; |
1831 |
|
|
1832 |
free(left); |
free(left); |
1833 |
} |
} |
1834 |
|
|
1853 |
cursor_pos = 0; |
cursor_pos = 0; |
1854 |
|
|
1855 |
while (ch != '\n') { |
while (ch != '\n') { |
1856 |
/* |
ch = debugger_readchar(); |
|
* TODO: This uses up 100% CPU, maybe that isn't too good. |
|
|
* The usleep() call might make it a tiny bit nicer on other |
|
|
* running processes, but it is still very ugly. |
|
|
*/ |
|
|
while ((ch = console_readchar(MAIN_CONSOLE)) < 0) { |
|
|
x11_check_event(debugger_emuls, debugger_n_emuls); |
|
|
usleep(2); |
|
|
} |
|
1857 |
|
|
1858 |
if ((ch == '\b' || ch == 127) && cursor_pos > 0) { |
if ((ch == '\b' || ch == 127) && cursor_pos > 0) { |
1859 |
/* Backspace. */ |
/* Backspace. */ |
1906 |
} |
} |
1907 |
} else if (ch == 11) { |
} else if (ch == 11) { |
1908 |
/* CTRL-K: Kill to end of line. */ |
/* CTRL-K: Kill to end of line. */ |
1909 |
for (i=0; i<MAX_CMD_LEN; i++) |
for (i=0; i<MAX_CMD_BUFLEN; i++) |
1910 |
console_makeavail(MAIN_CONSOLE, 4); /* :-) */ |
console_makeavail(MAIN_CONSOLE, 4); /* :-) */ |
1911 |
} else if (ch == 14 || ch == 16) { |
} else if (ch == 14 || ch == 16) { |
1912 |
/* CTRL-P: Previous line in the command history, |
/* CTRL-P: Previous line in the command history, |
1944 |
printf(" "); |
printf(" "); |
1945 |
for (i=cmd_len-1; i>=0; i--) |
for (i=cmd_len-1; i>=0; i--) |
1946 |
printf("\b \b"); |
printf("\b \b"); |
1947 |
strcpy(cmd, |
strlcpy(cmd, |
1948 |
last_cmd[read_from_index]); |
last_cmd[read_from_index], |
1949 |
|
MAX_CMD_BUFLEN); |
1950 |
cmd_len = strlen(cmd); |
cmd_len = strlen(cmd); |
1951 |
printf("%s", cmd); |
printf("%s", cmd); |
1952 |
cursor_pos = cmd_len; |
cursor_pos = cmd_len; |
1953 |
} |
} |
1954 |
} while (0); |
} while (0); |
1955 |
} else if (ch >= ' ' && cmd_len < MAX_CMD_LEN) { |
} else if (ch >= ' ' && cmd_len < MAX_CMD_BUFLEN-1) { |
1956 |
/* Visible character: */ |
/* Visible character: */ |
1957 |
memmove(cmd + cursor_pos + 1, cmd + cursor_pos, |
memmove(cmd + cursor_pos + 1, cmd + cursor_pos, |
1958 |
cmd_len - cursor_pos); |
cmd_len - cursor_pos); |
2101 |
int i, n, i_match, matchlen, cmd_len; |
int i, n, i_match, matchlen, cmd_len; |
2102 |
char *cmd; |
char *cmd; |
2103 |
|
|
2104 |
if (n_steps_left_before_interaction > 0) { |
if (debugger_n_steps_left_before_interaction > 0) { |
2105 |
n_steps_left_before_interaction --; |
debugger_n_steps_left_before_interaction --; |
2106 |
return; |
return; |
2107 |
} |
} |
2108 |
|
|
2123 |
if (cmd_len == 0) { |
if (cmd_len == 0) { |
2124 |
/* Special case for repeated commands: */ |
/* Special case for repeated commands: */ |
2125 |
if (repeat_cmd[0] != '\0') |
if (repeat_cmd[0] != '\0') |
2126 |
strcpy(cmd, repeat_cmd); |
strlcpy(cmd, repeat_cmd, MAX_CMD_BUFLEN); |
2127 |
else |
else |
2128 |
continue; |
continue; |
2129 |
} else { |
} else { |
2174 |
/* Check for a command name match: */ |
/* Check for a command name match: */ |
2175 |
n = i = i_match = 0; |
n = i = i_match = 0; |
2176 |
while (cmds[i].name != NULL) { |
while (cmds[i].name != NULL) { |
2177 |
if (strncasecmp(cmds[i].name, cmd, matchlen) == 0) { |
if (strncasecmp(cmds[i].name, cmd, matchlen) == 0 |
2178 |
|
&& cmds[i].f != NULL) { |
2179 |
cmds[i].tmp_flag = 1; |
cmds[i].tmp_flag = 1; |
2180 |
i_match = i; |
i_match = i; |
2181 |
n++; |
n++; |
2221 |
return; |
return; |
2222 |
} |
} |
2223 |
|
|
2224 |
|
gettimeofday(&debugger_machine->starttime, NULL); |
2225 |
|
debugger_machine->ncycles_since_gettimeofday = 0; |
2226 |
|
|
2227 |
single_step = 0; |
single_step = 0; |
2228 |
debugger_machine->instruction_trace = old_instruction_trace; |
debugger_machine->instruction_trace = old_instruction_trace; |
2229 |
debugger_machine->show_trace_tree = old_show_trace_tree; |
debugger_machine->show_trace_tree = old_show_trace_tree; |
2241 |
*/ |
*/ |
2242 |
void debugger_reset(void) |
void debugger_reset(void) |
2243 |
{ |
{ |
2244 |
n_steps_left_before_interaction = 0; |
debugger_n_steps_left_before_interaction = 0; |
2245 |
} |
} |
2246 |
|
|
2247 |
|
|
2272 |
debugger_machine = emuls[0]->machines[0]; |
debugger_machine = emuls[0]->machines[0]; |
2273 |
|
|
2274 |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
for (i=0; i<N_PREVIOUS_CMDS; i++) { |
2275 |
last_cmd[i] = malloc(MAX_CMD_LEN + 1); |
last_cmd[i] = malloc(MAX_CMD_BUFLEN); |
2276 |
if (last_cmd[i] == NULL) { |
if (last_cmd[i] == NULL) { |
2277 |
fprintf(stderr, "debugger_init(): out of memory\n"); |
fprintf(stderr, "debugger_init(): out of memory\n"); |
2278 |
exit(1); |
exit(1); |