--- trunk/src/debugger.c 2007/10/08 16:17:48 2 +++ trunk/src/debugger.c 2007/10/08 16:18:38 12 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: debugger.c,v 1.98 2005/03/20 20:27:26 debug Exp $ + * $Id: debugger.c,v 1.122 2005/08/11 09:43:40 debug Exp $ * * Single-step debugger. * @@ -54,6 +54,7 @@ #include #include +#include "bintrans.h" #include "console.h" #include "cpu.h" #include "device.h" @@ -81,6 +82,9 @@ int force_debugger_at_exit = 0; int show_opcode_statistics = 0; +volatile int single_step_breakpoint = 0; +int debugger_n_steps_left_before_interaction = 0; + int old_instruction_trace = 0; int old_quiet_mode = 0; int old_show_trace_tree = 0; @@ -98,14 +102,13 @@ static struct machine *debugger_machine; static int exit_debugger; -static int n_steps_left_before_interaction = 0; -#define MAX_CMD_LEN 70 +#define MAX_CMD_BUFLEN 72 #define N_PREVIOUS_CMDS 150 static char *last_cmd[N_PREVIOUS_CMDS]; static int last_cmd_index; -static char repeat_cmd[MAX_CMD_LEN + 1]; +static char repeat_cmd[MAX_CMD_BUFLEN]; #define MAGIC_UNTOUCHED 0x98ca76c2ffcc0011ULL @@ -114,6 +117,24 @@ /* + * 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. + */ +char debugger_readchar(void) +{ + int ch; + while ((ch = console_readchar(MAIN_CONSOLE)) < 0) { + x11_check_event(debugger_emuls, debugger_n_emuls); + usleep(1); + } + return ch; +} + + +/* * debugger_activate(): * * This is a signal handler for CTRL-C. It shouldn't be called directly, @@ -127,7 +148,7 @@ if (single_step) { /* Already in the debugger. Do nothing. */ int i; - for (i=0; i> 32) == 0 && (*valuep) & 0x80000000ULL) - printf("WARNING: The value is not sign-extended. " - "Is this what you intended?\n"); + 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] == '@'; @@ -233,7 +261,7 @@ fprintf(stderr, "out of memory in debugger\n"); exit(1); } - strcpy(sn, name); + strlcpy(sn, name, strlen(name)+1); /* Is there a '+' in there? Then treat that as an offset: */ p = strchr(sn, '+'); @@ -273,8 +301,11 @@ */ static void show_breakpoint(struct machine *m, int i) { - printf("%3i: 0x%016llx", i, - (long long)m->breakpoint_addr[i]); + printf("%3i: 0x", i); + if (m->cpus[0]->is_32bit) + printf("%08x", (int)m->breakpoint_addr[i]); + else + printf("%016llx", (long long)m->breakpoint_addr[i]); if (m->breakpoint_string[i] != NULL) printf(" (%s)", m->breakpoint_string[i]); if (m->breakpoint_flags[i]) @@ -336,11 +367,17 @@ m->breakpoint_flags[i] = m->breakpoint_flags[i+1]; } m->n_breakpoints --; + + /* Clear translations: */ + for (i=0; incpus; i++) + if (m->cpus[i]->translation_cache != NULL) + cpu_create_or_reset_tc(m->cpus[i]); return; } if (strncmp(cmd_line, "add ", 4) == 0) { uint64_t tmp; + size_t breakpoint_buf_len; if (m->n_breakpoints >= MAX_BREAKPOINTS) { printf("Too many breakpoints. (You need to recompile" @@ -357,17 +394,24 @@ return; } - m->breakpoint_string[i] = malloc(strlen(cmd_line+4) + 1); + breakpoint_buf_len = strlen(cmd_line+4) + 1; + m->breakpoint_string[i] = malloc(breakpoint_buf_len); if (m->breakpoint_string[i] == NULL) { printf("out of memory in debugger_cmd_breakpoint()\n"); exit(1); } - strcpy(m->breakpoint_string[i], cmd_line+4); + strlcpy(m->breakpoint_string[i], cmd_line+4, + breakpoint_buf_len); m->breakpoint_addr[i] = tmp; m->breakpoint_flags[i] = 0; m->n_breakpoints ++; show_breakpoint(m, i); + + /* Clear translations: */ + for (i=0; incpus; i++) + if (m->cpus[i]->translation_cache != NULL) + cpu_create_or_reset_tc(m->cpus[i]); return; } @@ -380,6 +424,8 @@ */ static void debugger_cmd_bintrans(struct machine *m, char *cmd_line) { + int i; + if (*cmd_line == '\0') goto printstate; @@ -393,9 +439,11 @@ cmd_line++; /* Note: len 3 and 4, to include the NUL char. */ - if (strncasecmp(cmd_line, "on", 3) == 0) + if (strncasecmp(cmd_line, "on", 3) == 0) { m->bintrans_enable = 1; - else if (strncasecmp(cmd_line, "off", 4) == 0) + for (i=0; incpus; i++) + bintrans_restart(m->cpus[i]); + } else if (strncasecmp(cmd_line, "off", 4) == 0) m->bintrans_enable = 0; else printf("syntax: bintrans [on|off]\n"); @@ -508,9 +556,9 @@ (long long)mem->dev_length[i]); if (mem->dev_flags[i]) { printf(" ("); - if (mem->dev_flags[i] & MEM_BINTRANS_OK) - printf("BINTRANS R"); - if (mem->dev_flags[i] & MEM_BINTRANS_WRITE_OK) + if (mem->dev_flags[i] & MEM_DYNTRANS_OK) + printf("DYNTRANS R"); + if (mem->dev_flags[i] & MEM_DYNTRANS_WRITE_OK) printf("+W"); printf(")"); } @@ -620,7 +668,10 @@ r = c->memory_rw(c, mem, addr, &buf[0], sizeof(buf), MEM_READ, CACHE_NONE | NO_EXCEPTIONS); - printf("0x%016llx ", (long long)addr); + if (c->is_32bit) + printf("0x%08x ", (int)addr); + else + printf("0x%016llx ", (long long)addr); if (r == MEMORY_ACCESS_FAILED) printf("(memory access failed)\n"); @@ -653,7 +704,7 @@ last_dump_addr = addr_end; - strcpy(repeat_cmd, "dump"); + strlcpy(repeat_cmd, "dump", MAX_CMD_BUFLEN); } @@ -795,15 +846,23 @@ printf("lookup for '%s' failed\n", cmd_line); return; } - printf("%s = 0x%016llx\n", cmd_line, (long long)newaddr); + printf("%s = 0x", cmd_line); + if (m->cpus[0]->is_32bit) + printf("%08x\n", (int)newaddr); + else + printf("%016llx\n", (long long)newaddr); return; } symbol = get_symbol_name(&m->symbol_context, addr, &offset); - if (symbol != NULL) - printf("0x%016llx = %s\n", (long long)addr, symbol); - else + if (symbol != NULL) { + if (m->cpus[0]->is_32bit) + printf("0x%08x", (int)addr); + else + printf("0x%016llx", (long long)addr); + printf(" = %s\n", symbol); + } else printf("lookup for '%s' failed\n", cmd_line); } @@ -830,6 +889,55 @@ /* + * debugger_cmd_ninstrs(): + */ +static void debugger_cmd_ninstrs(struct machine *m, char *cmd_line) +{ + int toggle = 1; + int previous_mode = m->show_nr_of_instructions; + + if (cmd_line[0] != '\0') { + while (cmd_line[0] != '\0' && cmd_line[0] == ' ') + cmd_line ++; + switch (cmd_line[0]) { + case '0': + toggle = 0; + m->show_nr_of_instructions = 0; + break; + case '1': + toggle = 0; + m->show_nr_of_instructions = 1; + break; + case 'o': + case 'O': + toggle = 0; + switch (cmd_line[1]) { + case 'n': + case 'N': + m->show_nr_of_instructions = 1; + break; + default: + m->show_nr_of_instructions = 0; + } + break; + default: + printf("syntax: trace [on|off]\n"); + return; + } + } + + if (toggle) + m->show_nr_of_instructions = !m->show_nr_of_instructions; + + printf("show_nr_of_instructions = %s", + m->show_nr_of_instructions? "ON" : "OFF"); + if (m->show_nr_of_instructions != previous_mode) + printf(" (was: %s)", previous_mode? "ON" : "OFF"); + printf("\n"); +} + + +/* * debugger_cmd_opcodestats(): */ static void debugger_cmd_opcodestats(struct machine *m, char *cmd_line) @@ -899,11 +1007,16 @@ printf("Multiple matches. Try prefixing with %%, $, or @.\n"); break; case NAME_PARSE_REGISTER: + printf("%s = 0x%llx\n", cmd_line, (long long)tmp); + break; case NAME_PARSE_SYMBOL: - printf("%s = 0x%016llx\n", cmd_line, (long long)tmp); + if (m->cpus[0]->is_32bit) + printf("%s = 0x%08x\n", cmd_line, (int)tmp); + else + printf("%s = 0x%016llx\n", cmd_line, (long long)tmp); break; case NAME_PARSE_NUMBER: - printf("0x%016llx\n", (long long)tmp); + printf("0x%llx\n", (long long)tmp); break; } } @@ -1012,7 +1125,11 @@ switch (put_type) { case 'b': a_byte = data; - printf("0x%016llx: %02x", (long long)addr, a_byte); + if (m->cpus[0]->is_32bit) + printf("0x%08x", (int)addr); + else + printf("0x%016llx", (long long)addr); + printf(": %02x", a_byte); if (data > 255) printf(" (NOTE: truncating %0llx)", (long long)data); res = m->cpus[0]->memory_rw(m->cpus[0], m->cpus[0]->mem, addr, @@ -1024,7 +1141,11 @@ case 'h': if ((data & 1) != 0) printf("WARNING: address isn't aligned\n"); - printf("0x%016llx: %04x", (long long)addr, (int)data); + if (m->cpus[0]->is_32bit) + printf("0x%08x", (int)addr); + else + printf("0x%016llx", (long long)addr); + printf(": %04x", (int)data); if (data > 0xffff) printf(" (NOTE: truncating %0llx)", (long long)data); res = store_16bit_word(m->cpus[0], addr, data); @@ -1035,7 +1156,11 @@ case 'w': if ((data & 3) != 0) printf("WARNING: address isn't aligned\n"); - printf("0x%016llx: %08x", (long long)addr, (int)data); + if (m->cpus[0]->is_32bit) + printf("0x%08x", (int)addr); + else + printf("0x%016llx", (long long)addr); + printf(": %08x", (int)data); if (data > 0xffffffff && (data >> 32) != 0 && (data >> 32) != 0xffffffff) printf(" (NOTE: truncating %0llx)", (long long)data); @@ -1047,7 +1172,11 @@ case 'd': if ((data & 7) != 0) printf("WARNING: address isn't aligned\n"); - printf("0x%016llx: %016llx", (long long)addr, (long long)data); + if (m->cpus[0]->is_32bit) + printf("0x%08x", (int)addr); + else + printf("0x%016llx", (long long)addr); + printf(": %016llx", (long long)data); res = store_64bit_word(m->cpus[0], addr, data); if (!res) printf(" FAILED!\n"); @@ -1197,12 +1326,12 @@ } } - n_steps_left_before_interaction = n - 1; + debugger_n_steps_left_before_interaction = n - 1; /* Special hack, see debugger() for more info. */ exit_debugger = -1; - strcpy(repeat_cmd, "step"); + strlcpy(repeat_cmd, "step", MAX_CMD_BUFLEN); } @@ -1249,21 +1378,55 @@ */ static void debugger_cmd_trace(struct machine *m, char *cmd_line) { - if (*cmd_line) { - printf("syntax: trace\n"); - return; + int i, toggle = 1; + int previous_mode = old_show_trace_tree; + + if (cmd_line[0] != '\0') { + while (cmd_line[0] != '\0' && cmd_line[0] == ' ') + cmd_line ++; + switch (cmd_line[0]) { + case '0': + toggle = 0; + old_show_trace_tree = 0; + break; + case '1': + toggle = 0; + old_show_trace_tree = 1; + break; + case 'o': + case 'O': + toggle = 0; + switch (cmd_line[1]) { + case 'n': + case 'N': + old_show_trace_tree = 1; + break; + default: + old_show_trace_tree = 0; + } + break; + default: + printf("syntax: trace [on|off]\n"); + return; + } } - old_show_trace_tree = 1 - old_show_trace_tree; - printf("show_trace_tree = %s\n", old_show_trace_tree? "ON" : "OFF"); + if (toggle) + old_show_trace_tree = 1 - old_show_trace_tree; + + printf("show_trace_tree = %s", old_show_trace_tree? "ON" : "OFF"); + if (old_show_trace_tree != previous_mode) + printf(" (was: %s)", previous_mode? "ON" : "OFF"); + printf("\n"); if (m->bintrans_enable && old_show_trace_tree) printf("NOTE: the trace tree functionality doesn't " "work very well with bintrans!\n"); - /* TODO: how to preserve quiet_mode? */ - old_quiet_mode = 0; - printf("quiet_mode = %s\n", old_quiet_mode? "ON" : "OFF"); + /* Clear translations: */ + for (i=0; incpus; i++) + if (m->cpus[i]->translation_cache != NULL) + cpu_create_or_reset_tc(m->cpus[i]); } @@ -1344,14 +1507,11 @@ addr = addr_start; - if ((addr & 3) != 0) - printf("WARNING! You entered an unaligned address.\n"); - ctrl_c = 0; while (addr < addr_end) { - int i, len; - unsigned char buf[25]; /* TODO: How long can an + unsigned int i, len; + unsigned char buf[17]; /* TODO: How long can an instruction be, on weird archs? */ memset(buf, 0, sizeof(buf)); @@ -1377,7 +1537,7 @@ last_unasm_addr = addr; - strcpy(repeat_cmd, "unassemble"); + strlcpy(repeat_cmd, "unassemble", MAX_CMD_BUFLEN); } @@ -1444,6 +1604,9 @@ { "machine", "", 0, debugger_cmd_machine, "print a summary of the current machine" }, + { "ninstrs", "[on|off]", 0, debugger_cmd_ninstrs, + "toggle (set or unset) show_nr_of_instructions" }, + { "opcodestats", "", 0, debugger_cmd_opcodestats, "show opcode statistics" }, @@ -1474,7 +1637,7 @@ { "tlbdump", "[cpuid][,r]", 0, debugger_cmd_tlbdump, "dump TLB contents (add ',r' for raw data)" }, - { "trace", "", 0, debugger_cmd_trace, + { "trace", "[on|off]", 0, debugger_cmd_trace, "toggle show_trace_tree on or off" }, { "unassemble", "[addr [endaddr]]", 0, debugger_cmd_unassemble, @@ -1483,6 +1646,9 @@ { "version", "", 0, debugger_cmd_version, "print version information" }, + /* Note: NULL handler. */ + { "x = expr", "", 0, NULL, "generic assignment" }, + { NULL, NULL, 0, NULL, NULL } }; @@ -1494,10 +1660,19 @@ * * NOTE: This is placed after the cmds[] array, because it needs to * access it. + * + * TODO: Command completion (ie just type "help s" for "help step"). */ static void debugger_cmd_help(struct machine *m, char *cmd_line) { - int i, j, max_name_len = 0; + int i, max_name_len = 0, only_one = 0, only_one_match = 0; + char *nlines_env = getenv("LINES"); + int nlines = atoi(nlines_env != NULL? nlines_env : "999999"); + int j, curlines; + + if (cmd_line[0] != '\0') { + only_one = 1; + } i = 0; while (cmds[i].name != NULL) { @@ -1509,12 +1684,25 @@ i++; } - printf("Available commands:\n"); + curlines = 0; + if (!only_one) { + printf("Available commands:\n"); + curlines++; + } i = 0; while (cmds[i].name != NULL) { char buf[100]; snprintf(buf, sizeof(buf), "%s", cmds[i].name); + + if (only_one) { + if (strcmp(cmds[i].name, cmd_line) != 0) { + i++; + continue; + } + only_one_match = 1; + } + if (cmds[i].args != NULL) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %s", cmds[i].args); @@ -1528,15 +1716,43 @@ printf(" %s\n", cmds[i].description); i++; + + curlines ++; + if (curlines >= nlines - 1) { + char ch; + printf("-- more --"); fflush(stdout); + ch = debugger_readchar(); + printf("\n"); + if (ch == 'q' || ch == 'Q') + return; + curlines = 0; + } + } + + if (only_one) { + if (!only_one_match) + printf("%s: no such command\n", cmd_line); + return; } - printf("Generic assignments: x = expr\n"); - printf("where x must be a register, and expr can be a register, a " - "numeric value, or\na symbol name (+ an optional numeric offset)." - " In case there are multiple\nmatches (ie a symbol that has the " - "same name as a register), you may add a\nprefix character as a " - "hint: '%%' for registers, '@' for symbols, and\n'$' for numeric" - " values. Use 0x for hexadecimal values.\n"); + /* TODO: generalize/refactor */ + curlines += 8; + if (curlines > nlines - 1) { + char ch; + printf("-- more --"); fflush(stdout); + ch = debugger_readchar(); + printf("\n"); + if (ch == 'q' || ch == 'Q') + return; + curlines = 0; + } + + printf("\nIn generic assignments, x must be a register, and expr can be" + " a register, a\nnumeric value, or a symbol name (+ an optional " + "numeric offset). In case there\nare multiple matches (i.e. a " + "symbol that has the same name as a register), you\nmay add a " + "prefix character as a hint: '%%' for registers, '@' for symbols," + " and\n'$' for numeric values. Use 0x for hexadecimal values.\n"); } @@ -1553,13 +1769,14 @@ char *left, *right; int res_left, res_right; uint64_t tmp; + uint64_t old_pc = m->cpus[0]->pc; /* TODO: multiple cpus? */ - left = malloc(strlen(cmd) + 1); + left = malloc(MAX_CMD_BUFLEN); if (left == NULL) { fprintf(stderr, "out of memory in debugger_assignment()\n"); exit(1); } - strcpy(left, cmd); + strlcpy(left, cmd, MAX_CMD_BUFLEN); right = strchr(left, '='); if (right == NULL) { fprintf(stderr, "internal error in the debugger\n"); @@ -1603,6 +1820,15 @@ } } + /* + * If the PC has changed, then release any breakpoint we were + * currently stopped at. + * + * TODO: multiple cpus? + */ + if (old_pc != m->cpus[0]->pc) + single_step_breakpoint = 0; + free(left); } @@ -1627,15 +1853,7 @@ cursor_pos = 0; while (ch != '\n') { - /* - * 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); - } + ch = debugger_readchar(); if ((ch == '\b' || ch == 127) && cursor_pos > 0) { /* Backspace. */ @@ -1688,7 +1906,7 @@ } } else if (ch == 11) { /* CTRL-K: Kill to end of line. */ - for (i=0; i=0; i--) printf("\b \b"); - strcpy(cmd, - last_cmd[read_from_index]); + strlcpy(cmd, + last_cmd[read_from_index], + MAX_CMD_BUFLEN); cmd_len = strlen(cmd); printf("%s", cmd); cursor_pos = cmd_len; } } while (0); - } else if (ch >= ' ' && cmd_len < MAX_CMD_LEN) { + } else if (ch >= ' ' && cmd_len < MAX_CMD_BUFLEN-1) { /* Visible character: */ memmove(cmd + cursor_pos + 1, cmd + cursor_pos, cmd_len - cursor_pos); @@ -1882,8 +2101,8 @@ int i, n, i_match, matchlen, cmd_len; char *cmd; - if (n_steps_left_before_interaction > 0) { - n_steps_left_before_interaction --; + if (debugger_n_steps_left_before_interaction > 0) { + debugger_n_steps_left_before_interaction --; return; } @@ -1904,7 +2123,7 @@ if (cmd_len == 0) { /* Special case for repeated commands: */ if (repeat_cmd[0] != '\0') - strcpy(cmd, repeat_cmd); + strlcpy(cmd, repeat_cmd, MAX_CMD_BUFLEN); else continue; } else { @@ -1955,7 +2174,8 @@ /* Check for a command name match: */ n = i = i_match = 0; while (cmds[i].name != NULL) { - if (strncasecmp(cmds[i].name, cmd, matchlen) == 0) { + if (strncasecmp(cmds[i].name, cmd, matchlen) == 0 + && cmds[i].f != NULL) { cmds[i].tmp_flag = 1; i_match = i; n++; @@ -2001,6 +2221,9 @@ return; } + gettimeofday(&debugger_machine->starttime, NULL); + debugger_machine->ncycles_since_gettimeofday = 0; + single_step = 0; debugger_machine->instruction_trace = old_instruction_trace; debugger_machine->show_trace_tree = old_show_trace_tree; @@ -2018,7 +2241,7 @@ */ void debugger_reset(void) { - n_steps_left_before_interaction = 0; + debugger_n_steps_left_before_interaction = 0; } @@ -2049,7 +2272,7 @@ debugger_machine = emuls[0]->machines[0]; for (i=0; i