/[gxemul]/trunk/src/debugger.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/src/debugger.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6 - (hide annotations)
Mon Oct 8 16:18:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 47221 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.772 2005/06/04 12:02:16 debug Exp $
20050428	Disabling the "-fmove-all-movables" option in the configure
		script, because it causes the compile to fail on OpenBSD/sgi.
20050502	Minor updates.
20050503	Removing the WRT54G mode (it was bogus anyway), and adding a
		comment about Windows NT for MIPS in doc/experiments.html.
		Minor updates to the x86 instruction decoding.
20050504	Adding some more x86 instructions.
		Adding support for reading files from ISO9660 CDROMs (including
		gzipped files). It's an ugly hack, but it seems to work.
		Various other minor updates (dev_vga.c, pc_bios.c etc).
20050505	Some more x86-related updates.
		Beginning (what I hope will be) a major code cleanup phase.
		"bootris" (an x86 bootsector) runs :-)
20050506	Adding some more x86 instructions.
20050507	tmpnam => mkstemp.
		Working on a hack to allow VGA charcells to be shown even when
		not running with X11.
		Adding more x86 instructions.
20050508	x86 32-bit SIB addressing fix, and more instructions.
20050509	Adding more x86 instructions.
20050510	Minor documentation updates, and other updates (x86 stuff etc.)
20050511	More x86-related updates.
20050513	Various updates, mostly x86-related. (Trying to fix flag 
		calculation, factoring out the ugly shift/rotate code, and
		some other things.)
20050514	Adding support for loading some old i386 a.out executables.
		Finally beginning the cleanup of machine/PROM/bios dependant
		info.
		Some minor documentation updates.
		Trying to clean up ARCBIOS stuff a little.
20050515	Trying to make it possible to actually use more than one disk
		type per machine (floppy, ide, scsi).
		Trying to clean up the kbd vs PROM console stuff. (For PC and
		ARC emulation modes, mostly.)
		Beginning to add an 8259 interrupt controller, and connecting
		it to the x86 emulation.
20050516	The first x86 interrupts seem to work (keyboard stuff).
		Adding a 8253/8254 programmable interval timer skeleton.
		FreeDOS now reaches a command prompt and can be interacted
		with.
20050517	After some bugfixes, MS-DOS also (sometimes) reaches a
		command prompt now.
		Trying to fix the pckbc to work with MS-DOS' keyb.com, but no
		success yet.
20050518	Adding a simple 32-bit x86 MMU skeleton.
20050519	Some more work on the x86 stuff. (Beginning the work on paging,
		and various other fixes).
20050520	More updates. Working on dev_vga (4-bit graphics modes), adding
		40 columns support to the PC bios emulation.
		Trying to add support for resizing windows when switching
		between graphics modes.
20050521	Many more x86-related updates.
20050522	Correcting the initial stack pointer's sign-extension for
		ARCBIOS emulation (thanks to Alec Voropay for noticing the
		error).
		Continuing on the cleanup (ARCBIOS etc).
		dev_vga updates.
20050523	More x86 updates: trying to add some support for protected mode
		interrupts (via gate descriptors) and many other fixes.
		More ARCBIOS cleanup.
		Adding a device flag which indicates that reads cause no
		side-effects. (Useful for the "dump" command in the debugger,
		and other things.)
		Adding support for directly starting up x86 ELFs, skipping the
		bootloader stage. (Most ELFs, however, are not suitable for
		this.)
20050524	Adding simple 32-bit x86 TSS task switching, but no privilege
		level support yet.
		More work on dev_vga. A small "Copper bars" demo works. :-)
		Adding support for Trap Flag (single-step exceptions), at least
		in real mode, and various other x86-related fixes.
20050525	Adding a new disk image prefix (gH;S;) which can be used to
		override the default nr of heads and sectors per track.
20050527	Various bug fixes, more work on the x86 mode (stack change on
		interrupts between different priv.levels), and some minor
		documentation updates.
20050528	Various fixes (x86 stuff).
20050529	More x86 fixes. An OpenBSD/i386 bootfloppy reaches userland
		and can be interacted with (although there are problems with
		key repetition). NetBSD/i386 triggers a serious CISC-related
		problem: instruction fetches across page boundaries, where
		the later part isn't actually part of the instruction.
20050530	Various minor updates. (Documentation updates, etc.)
20050531	Adding some experimental code (experiments/new_test_*) which
		could be useful for dynamic (but not binary) translation in
		the future.
20050602	Adding a dummy ARM skeleton.
		Fixing the pckbc key repetition problem (by adding release
		scancodes for all keypresses).
20050603	Minor updates for the next release.
20050604	Release testing. Minor updates.

==============  RELEASE 0.3.3  ==============

20050604	There'll probably be a 0.3.3.1 release soon, with some very
		very tiny updates.


1 dpavlin 2 /*
2     * Copyright (C) 2003-2005 Anders Gavare. All rights reserved.
3     *
4     * Redistribution and use in source and binary forms, with or without
5     * modification, are permitted provided that the following conditions are met:
6     *
7     * 1. Redistributions of source code must retain the above copyright
8     * notice, this list of conditions and the following disclaimer.
9     * 2. Redistributions in binary form must reproduce the above copyright
10     * notice, this list of conditions and the following disclaimer in the
11     * documentation and/or other materials provided with the distribution.
12     * 3. The name of the author may not be used to endorse or promote products
13     * derived from this software without specific prior written permission.
14     *
15     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18     * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25     * SUCH DAMAGE.
26     *
27     *
28 dpavlin 6 * $Id: debugger.c,v 1.103 2005/05/13 14:26:29 debug Exp $
29 dpavlin 2 *
30     * Single-step debugger.
31     *
32     *
33     * TODO:
34     *
35     * This entire module is very much non-reentrant. :-/
36     *
37     * Add more functionality that already exists elsewhere in the emulator.
38     *
39     * More generic expression evaluator (for example + - * / between multiple
40     * terms), including _TAB COMPLETION_ of symbols and register names!
41     *
42     * Nicer looking output of register dumps, floating point registers,
43     * etc. Warn about weird/invalid register contents.
44     *
45     * Ctrl-C doesn't enter the debugger on some OSes (HP-UX?)...
46     *
47     * Many other TODOs.
48     */
49    
50     #include <ctype.h>
51     #include <signal.h>
52     #include <stdio.h>
53     #include <stdlib.h>
54     #include <string.h>
55     #include <unistd.h>
56    
57 dpavlin 4 #include "bintrans.h"
58 dpavlin 2 #include "console.h"
59     #include "cpu.h"
60     #include "device.h"
61     #include "debugger.h"
62     #include "diskimage.h"
63     #include "emul.h"
64     #include "machine.h"
65     #include "memory.h"
66     #include "misc.h"
67     #include "net.h"
68     #include "x11.h"
69    
70    
71     extern int extra_argc;
72     extern char **extra_argv;
73    
74     extern int quiet_mode;
75    
76    
77     /*
78     * Global debugger variables:
79     */
80    
81     volatile int single_step = 0;
82     int force_debugger_at_exit = 0;
83     int show_opcode_statistics = 0;
84    
85     int old_instruction_trace = 0;
86     int old_quiet_mode = 0;
87     int old_show_trace_tree = 0;
88    
89    
90     /*
91     * Private (global) debugger variables:
92     */
93    
94     static volatile int ctrl_c;
95    
96     static int debugger_n_emuls;
97     static struct emul **debugger_emuls;
98     static struct emul *debugger_emul;
99     static struct machine *debugger_machine;
100    
101     static int exit_debugger;
102     static int n_steps_left_before_interaction = 0;
103    
104     #define MAX_CMD_LEN 70
105     #define N_PREVIOUS_CMDS 150
106     static char *last_cmd[N_PREVIOUS_CMDS];
107     static int last_cmd_index;
108    
109     static char repeat_cmd[MAX_CMD_LEN + 1];
110    
111     #define MAGIC_UNTOUCHED 0x98ca76c2ffcc0011ULL
112    
113     static uint64_t last_dump_addr = MAGIC_UNTOUCHED;
114     static uint64_t last_unasm_addr = MAGIC_UNTOUCHED;
115    
116    
117     /*
118     * debugger_activate():
119     *
120     * This is a signal handler for CTRL-C. It shouldn't be called directly,
121     * but setup code in emul.c sets the CTRL-C signal handler to use this
122     * function.
123     */
124     void debugger_activate(int x)
125     {
126     ctrl_c = 1;
127    
128     if (single_step) {
129     /* Already in the debugger. Do nothing. */
130     int i;
131     for (i=0; i<MAX_CMD_LEN+1; i++)
132     console_makeavail(MAIN_CONSOLE, '\b');
133     console_makeavail(MAIN_CONSOLE, ' ');
134     console_makeavail(MAIN_CONSOLE, '\n');
135     printf("^C");
136     fflush(stdout);
137     } else {
138     /* Enter the single step debugger. */
139     single_step = 1;
140    
141     /* Discard any chars in the input queue: */
142     while (console_charavail(MAIN_CONSOLE))
143     console_readchar(MAIN_CONSOLE);
144     }
145    
146     /* Clear the repeat-command buffer: */
147     repeat_cmd[0] = '\0';
148    
149     /* Reactivate the signal handler: */
150     signal(SIGINT, debugger_activate);
151     }
152    
153    
154     /*
155     * debugger_parse_name():
156     *
157     * This function reads a string, and tries to match it to a register name,
158     * a symbol, or treat it as a decimal numeric value.
159     *
160     * Some examples:
161     *
162     * "0x7fff1234" ==> numeric value (hex, in this case)
163     * "pc", "r5", "hi", "t4" ==> register (CPU dependant)
164     * "memcpy+64" ==> symbol (plus offset)
165     *
166     * Register names can be preceeded by "x:" where x is the CPU number. (CPU
167     * 0 is assumed by default.)
168     *
169     * To force detection of different types, a character can be added in front of
170     * the name: "$" for numeric values, "%" for registers, and "@" for symbols.
171     *
172     * Return value is:
173     *
174     * NAME_PARSE_NOMATCH no match
175     * NAME_PARSE_MULTIPLE multiple matches
176     *
177     * or one of these (and then *valuep is read or written, depending on
178     * the writeflag):
179     *
180     * NAME_PARSE_REGISTER a register
181     * NAME_PARSE_NUMBER a hex number
182     * NAME_PARSE_SYMBOL a symbol
183     */
184     #define NAME_PARSE_NOMATCH 0
185     #define NAME_PARSE_MULTIPLE 1
186     #define NAME_PARSE_REGISTER 2
187     #define NAME_PARSE_NUMBER 3
188     #define NAME_PARSE_SYMBOL 4
189     static int debugger_parse_name(struct machine *m, char *name, int writeflag,
190     uint64_t *valuep)
191     {
192     int match_register = 0, match_symbol = 0, match_numeric = 0;
193     int skip_register, skip_numeric, skip_symbol;
194    
195     if (m == NULL || name == NULL) {
196     fprintf(stderr, "debugger_parse_name(): NULL ptr\n");
197     exit(1);
198     }
199    
200     /* Warn about non-signextended values: */
201     if (writeflag &&
202     ((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL)
203     printf("WARNING: The value is not sign-extended. "
204     "Is this what you intended?\n");
205    
206     skip_register = name[0] == '$' || name[0] == '@';
207     skip_numeric = name[0] == '%' || name[0] == '@';
208     skip_symbol = name[0] == '$' || name[0] == '%';
209    
210     /* Check for a register match: */
211     if (!skip_register && strlen(name) >= 1)
212     cpu_register_match(m, name, writeflag, valuep,
213     &match_register);
214    
215     /* Check for a number match: */
216     if (!skip_numeric && isdigit((int)name[0])) {
217     uint64_t x;
218     x = strtoull(name, NULL, 0);
219     if (writeflag) {
220     printf("You cannot assign like that.\n");
221     } else
222     *valuep = x;
223     match_numeric = 1;
224     }
225    
226     /* Check for a symbol match: */
227     if (!skip_symbol) {
228     int res;
229     char *p, *sn;
230     uint64_t newaddr, ofs = 0;
231    
232     sn = malloc(strlen(name) + 1);
233     if (sn == NULL) {
234     fprintf(stderr, "out of memory in debugger\n");
235     exit(1);
236     }
237     strcpy(sn, name);
238    
239     /* Is there a '+' in there? Then treat that as an offset: */
240     p = strchr(sn, '+');
241     if (p != NULL) {
242     *p = '\0';
243     ofs = strtoull(p+1, NULL, 0);
244     }
245    
246     res = get_symbol_addr(&m->symbol_context, sn, &newaddr);
247     if (res) {
248     if (writeflag) {
249     printf("You cannot assign like that.\n");
250     } else
251     *valuep = newaddr + ofs;
252     match_symbol = 1;
253     }
254    
255     free(sn);
256     }
257    
258     if (match_register + match_symbol + match_numeric > 1)
259     return NAME_PARSE_MULTIPLE;
260    
261     if (match_register)
262     return NAME_PARSE_REGISTER;
263     if (match_numeric)
264     return NAME_PARSE_NUMBER;
265     if (match_symbol)
266     return NAME_PARSE_SYMBOL;
267    
268     return NAME_PARSE_NOMATCH;
269     }
270    
271    
272     /*
273     * show_breakpoint():
274     */
275     static void show_breakpoint(struct machine *m, int i)
276     {
277     printf("%3i: 0x%016llx", i,
278     (long long)m->breakpoint_addr[i]);
279     if (m->breakpoint_string[i] != NULL)
280     printf(" (%s)", m->breakpoint_string[i]);
281     if (m->breakpoint_flags[i])
282     printf(": flags=0x%x", m->breakpoint_flags[i]);
283     printf("\n");
284     }
285    
286    
287     /****************************************************************************/
288    
289    
290     /*
291     * debugger_cmd_breakpoint():
292     *
293     * TODO: automagic "expansion" for the subcommand names (s => show).
294     */
295     static void debugger_cmd_breakpoint(struct machine *m, char *cmd_line)
296     {
297     int i, res;
298    
299     while (cmd_line[0] != '\0' && cmd_line[0] == ' ')
300     cmd_line ++;
301    
302     if (cmd_line[0] == '\0') {
303     printf("syntax: breakpoint subcmd [args...]\n");
304     printf("Available subcmds (and args) are:\n");
305     printf(" add addr add a breakpoint for address addr\n");
306     printf(" delete x delete breakpoint nr x\n");
307     printf(" show show current breakpoints\n");
308     return;
309     }
310    
311     if (strcmp(cmd_line, "show") == 0) {
312     if (m->n_breakpoints == 0)
313     printf("No breakpoints set.\n");
314     for (i=0; i<m->n_breakpoints; i++)
315     show_breakpoint(m, i);
316     return;
317     }
318    
319     if (strncmp(cmd_line, "delete ", 7) == 0) {
320     int x = atoi(cmd_line + 7);
321    
322     if (m->n_breakpoints == 0) {
323     printf("No breakpoints set.\n");
324     return;
325     }
326     if (x < 0 || x >= m->n_breakpoints) {
327     printf("Invalid breakpoint nr %i. Use 'breakpoint "
328     "show' to see the current breakpoints.\n", x);
329     return;
330     }
331    
332     free(m->breakpoint_string[x]);
333    
334     for (i=x; i<m->n_breakpoints-1; i++) {
335     m->breakpoint_addr[i] = m->breakpoint_addr[i+1];
336     m->breakpoint_string[i] = m->breakpoint_string[i+1];
337     m->breakpoint_flags[i] = m->breakpoint_flags[i+1];
338     }
339     m->n_breakpoints --;
340     return;
341     }
342    
343     if (strncmp(cmd_line, "add ", 4) == 0) {
344     uint64_t tmp;
345    
346     if (m->n_breakpoints >= MAX_BREAKPOINTS) {
347     printf("Too many breakpoints. (You need to recompile"
348     " gxemul to increase this. Max = %i.)\n",
349     MAX_BREAKPOINTS);
350     return;
351     }
352    
353     i = m->n_breakpoints;
354    
355     res = debugger_parse_name(m, cmd_line + 4, 0, &tmp);
356     if (!res) {
357     printf("Couldn't parse '%s'\n", cmd_line + 4);
358     return;
359     }
360    
361     m->breakpoint_string[i] = malloc(strlen(cmd_line+4) + 1);
362     if (m->breakpoint_string[i] == NULL) {
363     printf("out of memory in debugger_cmd_breakpoint()\n");
364     exit(1);
365     }
366     strcpy(m->breakpoint_string[i], cmd_line+4);
367     m->breakpoint_addr[i] = tmp;
368     m->breakpoint_flags[i] = 0;
369    
370     m->n_breakpoints ++;
371     show_breakpoint(m, i);
372     return;
373     }
374    
375     printf("Unknown breakpoint subcommand.\n");
376     }
377    
378    
379     /*
380     * debugger_cmd_bintrans():
381     */
382     static void debugger_cmd_bintrans(struct machine *m, char *cmd_line)
383     {
384 dpavlin 4 int i;
385    
386 dpavlin 2 if (*cmd_line == '\0')
387     goto printstate;
388    
389     if (!m->bintrans_enabled_from_start) {
390     printf("You must have enabled bintrans from the start of the "
391     "simulation.\nIt is not possible to turn on afterwards.\n");
392     return;
393     }
394    
395     while (*cmd_line == ' ')
396     cmd_line++;
397    
398     /* Note: len 3 and 4, to include the NUL char. */
399 dpavlin 4 if (strncasecmp(cmd_line, "on", 3) == 0) {
400 dpavlin 2 m->bintrans_enable = 1;
401 dpavlin 4 for (i=0; i<m->ncpus; i++)
402     bintrans_restart(m->cpus[i]);
403     } else if (strncasecmp(cmd_line, "off", 4) == 0)
404 dpavlin 2 m->bintrans_enable = 0;
405     else
406     printf("syntax: bintrans [on|off]\n");
407    
408     printstate:
409     printf("bintrans is now %s%s\n",
410     m->bintrans_enable? "ENABLED" : "disabled",
411     m->old_bintrans_enable? " (using the OLD bintrans system)" : "");
412     }
413    
414    
415     /*
416     * debugger_cmd_continue():
417     */
418     static void debugger_cmd_continue(struct machine *m, char *cmd_line)
419     {
420     if (*cmd_line) {
421     printf("syntax: continue\n");
422     return;
423     }
424    
425     exit_debugger = 1;
426     }
427    
428    
429     /*
430     * debugger_cmd_device():
431     */
432     static void debugger_cmd_device(struct machine *m, char *cmd_line)
433     {
434     int i, j;
435     struct memory *mem;
436     struct cpu *c;
437    
438     if (cmd_line[0] == '\0')
439     goto return_help;
440    
441     if (m->cpus == NULL) {
442     printf("No cpus (?)\n");
443     return;
444     }
445     c = m->cpus[m->bootstrap_cpu];
446     if (c == NULL) {
447     printf("m->cpus[m->bootstrap_cpu] = NULL\n");
448     return;
449     }
450     mem = m->cpus[m->bootstrap_cpu]->mem;
451    
452     if (m->cpus == NULL) {
453     printf("No cpus (?)\n");
454     return;
455     }
456     c = m->cpus[m->bootstrap_cpu];
457     if (c == NULL) {
458     printf("m->cpus[m->bootstrap_cpu] = NULL\n");
459     return;
460     }
461     mem = m->cpus[m->bootstrap_cpu]->mem;
462    
463     if (strcmp(cmd_line, "all") == 0) {
464     device_dumplist();
465     } else if (strncmp(cmd_line, "add ", 4) == 0) {
466     device_add(m, cmd_line+4);
467     } else if (strncmp(cmd_line, "remove ", 7) == 0) {
468     i = atoi(cmd_line + 7);
469     if (i==0 && cmd_line[7]!='0') {
470     printf("Weird device number. Use 'device list'.\n");
471     } else
472     memory_device_remove(m->memory, i);
473     } else if (strncmp(cmd_line, "state ", 6) == 0) {
474     i = atoi(cmd_line + 6);
475     if (i < 0 || i >= mem->n_mmapped_devices) {
476     printf("No devices with that id.\n");
477     return;
478     }
479    
480     if (mem->dev_f_state[i] == NULL) {
481     printf("No state function has been implemented yet "
482     "for that device type.\n");
483     return;
484     }
485    
486     for (j=0; ; j++) {
487     int type;
488     char *name;
489     void *data;
490     size_t len;
491     int res = mem->dev_f_state[i](c, mem,
492     mem->dev_extra[i], 0, j, &type, &name, &data, &len);
493     if (!res)
494     break;
495     printf("%2i:%30s = (", j, name);
496     switch (type) {
497     case DEVICE_STATE_TYPE_INT:
498     printf("int) %i", *((int *)data));
499     break;
500     default:
501     printf("unknown)");
502     }
503     printf("\n");
504     }
505     } else if (strcmp(cmd_line, "list") == 0) {
506     if (mem->n_mmapped_devices == 0)
507     printf("No memory-mapped devices in this machine.\n");
508    
509     for (i=0; i<mem->n_mmapped_devices; i++) {
510     printf("%2i: %25s @ 0x%011llx, len = 0x%llx",
511     i, mem->dev_name[i],
512     (long long)mem->dev_baseaddr[i],
513     (long long)mem->dev_length[i]);
514     if (mem->dev_flags[i]) {
515     printf(" (");
516     if (mem->dev_flags[i] & MEM_BINTRANS_OK)
517     printf("BINTRANS R");
518     if (mem->dev_flags[i] & MEM_BINTRANS_WRITE_OK)
519     printf("+W");
520     printf(")");
521     }
522     printf("\n");
523     }
524     } else
525     goto return_help;
526    
527     return;
528    
529     return_help:
530     printf("syntax: devices cmd [...]\n");
531     printf("Available cmds are:\n");
532     printf(" add name_and_params add a device to the current "
533     "machine\n");
534     printf(" all list all registered devices\n");
535     printf(" list list memory-mapped devices in the"
536     " current machine\n");
537     printf(" remove x remove device nr x from the "
538     "current machine\n");
539     printf(" state x show state of device nr x in"
540     " the current machine\n");
541     }
542    
543    
544     /*
545     * debugger_cmd_dump():
546     *
547     * Dump emulated memory in hex and ASCII.
548     *
549     * syntax: dump [addr [endaddr]]
550     */
551     static void debugger_cmd_dump(struct machine *m, char *cmd_line)
552     {
553     uint64_t addr, addr_start, addr_end;
554     struct cpu *c;
555     struct memory *mem;
556     char *p = NULL;
557     int x, r;
558    
559     if (cmd_line[0] != '\0') {
560     uint64_t tmp;
561     char *tmps = strdup(cmd_line);
562    
563     /* addr: */
564     p = strchr(tmps, ' ');
565     if (p != NULL)
566     *p = '\0';
567     r = debugger_parse_name(m, tmps, 0, &tmp);
568     free(tmps);
569    
570     if (r == NAME_PARSE_NOMATCH || r == NAME_PARSE_MULTIPLE) {
571     printf("Unparsable address: %s\n", cmd_line);
572     return;
573     } else {
574     last_dump_addr = tmp;
575     }
576    
577     p = strchr(cmd_line, ' ');
578     }
579    
580     addr_start = last_dump_addr;
581    
582     if (addr_start == MAGIC_UNTOUCHED) {
583     uint64_t tmp;
584     int match_register = 0;
585     cpu_register_match(m, "pc", 0, &tmp, &match_register);
586     if (match_register) {
587     addr_start = tmp;
588     } else {
589     printf("No starting address.\n");
590     return;
591     }
592     }
593    
594     addr_end = addr_start + 16 * 16;
595    
596     /* endaddr: */
597     if (p != NULL) {
598     while (*p == ' ' && *p)
599     p++;
600     r = debugger_parse_name(m, p, 0, &addr_end);
601     if (r == NAME_PARSE_NOMATCH || r == NAME_PARSE_MULTIPLE) {
602     printf("Unparsable address: %s\n", cmd_line);
603     return;
604     }
605     }
606    
607     if (m->cpus == NULL) {
608     printf("No cpus (?)\n");
609     return;
610     }
611     c = m->cpus[m->bootstrap_cpu];
612     if (c == NULL) {
613     printf("m->cpus[m->bootstrap_cpu] = NULL\n");
614     return;
615     }
616     mem = m->cpus[m->bootstrap_cpu]->mem;
617    
618     addr = addr_start & ~0xf;
619    
620     ctrl_c = 0;
621    
622     while (addr < addr_end) {
623     unsigned char buf[16];
624     memset(buf, 0, sizeof(buf));
625     r = c->memory_rw(c, mem, addr, &buf[0], sizeof(buf),
626     MEM_READ, CACHE_NONE | NO_EXCEPTIONS);
627    
628     printf("0x%016llx ", (long long)addr);
629    
630     if (r == MEMORY_ACCESS_FAILED)
631     printf("(memory access failed)\n");
632     else {
633     for (x=0; x<16; x++) {
634     if (addr + x >= addr_start &&
635     addr + x < addr_end)
636     printf("%02x%s", buf[x],
637     (x&3)==3? " " : "");
638     else
639     printf(" %s", (x&3)==3? " " : "");
640     }
641     printf(" ");
642     for (x=0; x<16; x++) {
643     if (addr + x >= addr_start &&
644     addr + x < addr_end)
645     printf("%c", (buf[x]>=' ' &&
646     buf[x]<127)? buf[x] : '.');
647     else
648     printf(" ");
649     }
650     printf("\n");
651     }
652    
653     if (ctrl_c)
654     return;
655    
656     addr += sizeof(buf);
657     }
658    
659     last_dump_addr = addr_end;
660    
661     strcpy(repeat_cmd, "dump");
662     }
663    
664    
665     /*
666     * debugger_cmd_emuls():
667     *
668     * Dump info about all current emuls.
669     */
670     static void debugger_cmd_emuls(struct machine *m, char *cmd_line)
671     {
672     int i, iadd = 4;
673    
674     if (*cmd_line) {
675     printf("syntax: emuls\n");
676     return;
677     }
678    
679     for (i=0; i<debugger_n_emuls; i++) {
680     struct emul *e = debugger_emuls[i];
681    
682     if (e == NULL)
683     continue;
684    
685     debug("emulation %i: \"%s\"\n", i,
686     e->name == NULL? "(no name)" : e->name);
687     debug_indentation(iadd);
688    
689     emul_dumpinfo(e);
690    
691     debug_indentation(-iadd);
692     }
693     }
694    
695    
696     /*
697     * debugger_cmd_focus():
698     *
699     * Changes focus to specific machine (in a specific emulation).
700     */
701     static void debugger_cmd_focus(struct machine *m, char *cmd_line)
702     {
703     int x = -1, y = -1;
704     char *p;
705    
706     if (!cmd_line[0]) {
707     printf("syntax: focus x[,y]\n");
708     printf("where x and y are integers as reported by the"
709     " 'emuls' command\n");
710     goto print_current_focus_and_return;
711     }
712    
713     x = atoi(cmd_line);
714     p = strchr(cmd_line, ',');
715     if (p == cmd_line) {
716     printf("No machine number specified?\n");
717     printf("syntax: focus x[,y]\n");
718     return;
719     }
720    
721     if (p != NULL)
722     y = atoi(p + 1);
723    
724     if (y != -1) {
725     /* Change emul: */
726     if (y < 0 || y >= debugger_n_emuls) {
727     printf("Invalid emul number: %i\n", y);
728     return;
729     }
730    
731     debugger_emul = debugger_emuls[y];
732    
733     /* This is just in case the machine change below fails... */
734     debugger_machine = debugger_emul->machines[0];
735     }
736    
737     /* Change machine: */
738     if (x < 0 || x >= debugger_emul->n_machines) {
739     printf("Invalid machine number: %i\n", x);
740     return;
741     }
742    
743     debugger_machine = debugger_emul->machines[x];
744    
745     print_current_focus_and_return:
746     printf("current emul: \"%s\"\n", debugger_emul->name == NULL?
747     "(no name)" : debugger_emul->name);
748     printf("current machine: \"%s\"\n", debugger_machine->name == NULL?
749     "(no name)" : debugger_machine->name);
750     }
751    
752    
753     /* This is defined below. */
754     static void debugger_cmd_help(struct machine *m, char *cmd_line);
755    
756    
757     /*
758     * debugger_cmd_itrace():
759     */
760     static void debugger_cmd_itrace(struct machine *m, char *cmd_line)
761     {
762     if (*cmd_line) {
763     printf("syntax: itrace\n");
764     return;
765     }
766    
767     old_instruction_trace = 1 - old_instruction_trace;
768     printf("instruction_trace = %s\n", old_instruction_trace? "ON":"OFF");
769     /* TODO: how to preserve quiet_mode? */
770     old_quiet_mode = 0;
771     printf("quiet_mode = %s\n", old_quiet_mode? "ON" : "OFF");
772     }
773    
774    
775     /*
776     * debugger_cmd_lookup():
777     */
778     static void debugger_cmd_lookup(struct machine *m, char *cmd_line)
779     {
780     uint64_t addr;
781     int res;
782     char *symbol;
783     uint64_t offset;
784    
785     if (cmd_line[0] == '\0') {
786     printf("syntax: lookup name|addr\n");
787     return;
788    
789     }
790    
791     /* Addresses never need to be given in decimal form anyway,
792     so assuming hex here will be ok. */
793     addr = strtoull(cmd_line, NULL, 16);
794    
795     if (addr == 0) {
796     uint64_t newaddr;
797     res = get_symbol_addr(&m->symbol_context,
798     cmd_line, &newaddr);
799     if (!res) {
800     printf("lookup for '%s' failed\n", cmd_line);
801     return;
802     }
803     printf("%s = 0x%016llx\n", cmd_line, (long long)newaddr);
804     return;
805     }
806    
807     symbol = get_symbol_name(&m->symbol_context, addr, &offset);
808    
809     if (symbol != NULL)
810     printf("0x%016llx = %s\n", (long long)addr, symbol);
811     else
812     printf("lookup for '%s' failed\n", cmd_line);
813     }
814    
815    
816     /*
817     * debugger_cmd_machine():
818     *
819     * Dump info about the currently focused machine.
820     */
821     static void debugger_cmd_machine(struct machine *m, char *cmd_line)
822     {
823     int iadd = 4;
824    
825     if (*cmd_line) {
826     printf("syntax: machine\n");
827     return;
828     }
829    
830     debug("machine \"%s\":\n", m->name);
831     debug_indentation(iadd);
832     machine_dumpinfo(m);
833     debug_indentation(-iadd);
834     }
835    
836    
837     /*
838     * debugger_cmd_opcodestats():
839     */
840     static void debugger_cmd_opcodestats(struct machine *m, char *cmd_line)
841     {
842     if (*cmd_line) {
843     printf("syntax: opcodestats\n");
844     return;
845     }
846    
847     if (!show_opcode_statistics) {
848     printf("You need to start the emulator "
849     "with -s, if you want to gather statistics.\n");
850     } else
851     cpu_show_full_statistics(m);
852     }
853    
854    
855     /*
856     * debugger_cmd_pause():
857     */
858     static void debugger_cmd_pause(struct machine *m, char *cmd_line)
859     {
860     int cpuid = -1;
861    
862     if (cmd_line[0] != '\0')
863     cpuid = atoi(cmd_line);
864     else {
865     printf("syntax: pause cpuid\n");
866     return;
867     }
868    
869     if (cpuid < 0 || cpuid >= m->ncpus) {
870     printf("cpu%i doesn't exist.\n", cpuid);
871     return;
872     }
873    
874     m->cpus[cpuid]->running ^= 1;
875    
876     printf("cpu%i (%s) in machine \"%s\" is now %s\n", cpuid,
877     m->cpus[cpuid]->name, m->name,
878     m->cpus[cpuid]->running? "RUNNING" : "STOPPED");
879     }
880    
881    
882     /*
883     * debugger_cmd_print():
884     */
885     static void debugger_cmd_print(struct machine *m, char *cmd_line)
886     {
887     int res;
888     uint64_t tmp;
889    
890     while (cmd_line[0] != '\0' && cmd_line[0] == ' ')
891     cmd_line ++;
892    
893     if (cmd_line[0] == '\0') {
894     printf("syntax: print expr\n");
895     return;
896     }
897    
898     res = debugger_parse_name(m, cmd_line, 0, &tmp);
899     switch (res) {
900     case NAME_PARSE_NOMATCH:
901     printf("No match.\n");
902     break;
903     case NAME_PARSE_MULTIPLE:
904     printf("Multiple matches. Try prefixing with %%, $, or @.\n");
905     break;
906     case NAME_PARSE_REGISTER:
907 dpavlin 6 printf("%s = 0x%llx\n", cmd_line, (long long)tmp);
908     break;
909 dpavlin 2 case NAME_PARSE_SYMBOL:
910     printf("%s = 0x%016llx\n", cmd_line, (long long)tmp);
911     break;
912     case NAME_PARSE_NUMBER:
913 dpavlin 6 printf("0x%llx\n", (long long)tmp);
914 dpavlin 2 break;
915     }
916     }
917    
918    
919     /*
920     * debugger_cmd_put():
921     */
922     static void debugger_cmd_put(struct machine *m, char *cmd_line)
923     {
924     static char put_type = ' '; /* Remembered across multiple calls. */
925     char copy[200];
926     int res, syntax_ok = 0;
927     char *p, *p2, *q = NULL;
928     uint64_t addr, data;
929     unsigned char a_byte;
930    
931     strncpy(copy, cmd_line, sizeof(copy));
932     copy[sizeof(copy)-1] = '\0';
933    
934     /* syntax: put [b|h|w|d|q] addr, data */
935    
936     p = strchr(copy, ',');
937     if (p != NULL) {
938     *p++ = '\0';
939     while (*p == ' ' && *p)
940     p++;
941     while (strlen(copy) >= 1 &&
942     copy[strlen(copy) - 1] == ' ')
943     copy[strlen(copy) - 1] = '\0';
944    
945     /* printf("L = '%s', R = '%s'\n", copy, p); */
946    
947     q = copy;
948     p2 = strchr(q, ' ');
949    
950     if (p2 != NULL) {
951     *p2 = '\0';
952     if (strlen(q) != 1) {
953     printf("Invalid type '%s'\n", q);
954     return;
955     }
956     put_type = *q;
957     q = p2 + 1;
958     }
959    
960     /* printf("type '%c', L '%s', R '%s'\n", put_type, q, p); */
961     syntax_ok = 1;
962     }
963    
964     if (!syntax_ok) {
965     printf("syntax: put [b|h|w|d|q] addr, data\n");
966     printf(" b byte (8 bits)\n");
967     printf(" h half-word (16 bits)\n");
968     printf(" w word (32 bits)\n");
969     printf(" d doubleword (64 bits)\n");
970     printf(" q quad-word (128 bits)\n");
971     return;
972     }
973    
974     if (put_type == ' ') {
975     printf("No type specified.\n");
976     return;
977     }
978    
979     /* here: q is the address, p is the data. */
980    
981     res = debugger_parse_name(m, q, 0, &addr);
982     switch (res) {
983     case NAME_PARSE_NOMATCH:
984     printf("Couldn't parse the address.\n");
985     return;
986     case NAME_PARSE_MULTIPLE:
987     printf("Multiple matches for the address."
988     " Try prefixing with %%, $, or @.\n");
989     return;
990     case NAME_PARSE_REGISTER:
991     case NAME_PARSE_SYMBOL:
992     case NAME_PARSE_NUMBER:
993     break;
994     default:
995     printf("INTERNAL ERROR in debugger.c.\n");
996     return;
997     }
998    
999     res = debugger_parse_name(m, p, 0, &data);
1000     switch (res) {
1001     case NAME_PARSE_NOMATCH:
1002     printf("Couldn't parse the data.\n");
1003     return;
1004     case NAME_PARSE_MULTIPLE:
1005     printf("Multiple matches for the data value."
1006     " Try prefixing with %%, $, or @.\n");
1007     return;
1008     case NAME_PARSE_REGISTER:
1009     case NAME_PARSE_SYMBOL:
1010     case NAME_PARSE_NUMBER:
1011     break;
1012     default:
1013     printf("INTERNAL ERROR in debugger.c.\n");
1014     return;
1015     }
1016    
1017     /* TODO: haha, maybe this should be refactored */
1018    
1019     switch (put_type) {
1020     case 'b':
1021     a_byte = data;
1022     printf("0x%016llx: %02x", (long long)addr, a_byte);
1023     if (data > 255)
1024     printf(" (NOTE: truncating %0llx)", (long long)data);
1025     res = m->cpus[0]->memory_rw(m->cpus[0], m->cpus[0]->mem, addr,
1026     &a_byte, 1, MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS);
1027     if (!res)
1028     printf(" FAILED!\n");
1029     printf("\n");
1030     return;
1031     case 'h':
1032     if ((data & 1) != 0)
1033     printf("WARNING: address isn't aligned\n");
1034     printf("0x%016llx: %04x", (long long)addr, (int)data);
1035     if (data > 0xffff)
1036     printf(" (NOTE: truncating %0llx)", (long long)data);
1037     res = store_16bit_word(m->cpus[0], addr, data);
1038     if (!res)
1039     printf(" FAILED!\n");
1040     printf("\n");
1041     return;
1042     case 'w':
1043     if ((data & 3) != 0)
1044     printf("WARNING: address isn't aligned\n");
1045     printf("0x%016llx: %08x", (long long)addr, (int)data);
1046     if (data > 0xffffffff && (data >> 32) != 0
1047     && (data >> 32) != 0xffffffff)
1048     printf(" (NOTE: truncating %0llx)", (long long)data);
1049     res = store_32bit_word(m->cpus[0], addr, data);
1050     if (!res)
1051     printf(" FAILED!\n");
1052     printf("\n");
1053     return;
1054     case 'd':
1055     if ((data & 7) != 0)
1056     printf("WARNING: address isn't aligned\n");
1057     printf("0x%016llx: %016llx", (long long)addr, (long long)data);
1058     res = store_64bit_word(m->cpus[0], addr, data);
1059     if (!res)
1060     printf(" FAILED!\n");
1061     printf("\n");
1062     return;
1063     case 'q':
1064     printf("quad-words: TODO\n");
1065     /* TODO */
1066     return;
1067     default:
1068     printf("Unimplemented type '%c'\n", put_type);
1069     return;
1070     }
1071     }
1072    
1073    
1074     /*
1075     * debugger_cmd_quiet():
1076     */
1077     static void debugger_cmd_quiet(struct machine *m, char *cmd_line)
1078     {
1079     int toggle = 1;
1080     int previous_mode = old_quiet_mode;
1081    
1082     if (cmd_line[0] != '\0') {
1083     while (cmd_line[0] != '\0' && cmd_line[0] == ' ')
1084     cmd_line ++;
1085     switch (cmd_line[0]) {
1086     case '0':
1087     toggle = 0;
1088     old_quiet_mode = 0;
1089     break;
1090     case '1':
1091     toggle = 0;
1092     old_quiet_mode = 1;
1093     break;
1094     case 'o':
1095     case 'O':
1096     toggle = 0;
1097     switch (cmd_line[1]) {
1098     case 'n':
1099     case 'N':
1100     old_quiet_mode = 1;
1101     break;
1102     default:
1103     old_quiet_mode = 0;
1104     }
1105     break;
1106     default:
1107     printf("syntax: quiet [on|off]\n");
1108     return;
1109     }
1110     }
1111    
1112     if (toggle)
1113     old_quiet_mode = 1 - old_quiet_mode;
1114    
1115     printf("quiet_mode = %s", old_quiet_mode? "ON" : "OFF");
1116     if (old_quiet_mode != previous_mode)
1117     printf(" (was: %s)", previous_mode? "ON" : "OFF");
1118     printf("\n");
1119     }
1120    
1121    
1122     /*
1123     * debugger_cmd_quit():
1124     */
1125     static void debugger_cmd_quit(struct machine *m, char *cmd_line)
1126     {
1127     int i, j, k;
1128     struct emul *e;
1129    
1130     if (*cmd_line) {
1131     printf("syntax: quit\n");
1132     return;
1133     }
1134    
1135     for (i=0; i<debugger_n_emuls; i++) {
1136     single_step = 0;
1137    
1138     e = debugger_emuls[i];
1139     force_debugger_at_exit = 0;
1140    
1141     for (j=0; j<e->n_machines; j++) {
1142     struct machine *m = e->machines[j];
1143    
1144     for (k=0; k<m->ncpus; k++)
1145     m->cpus[k]->running = 0;
1146    
1147     m->exit_without_entering_debugger = 1;
1148     }
1149     }
1150    
1151     exit_debugger = 1;
1152     }
1153    
1154    
1155     /*
1156     * debugger_cmd_reg():
1157     */
1158     static void debugger_cmd_reg(struct machine *m, char *cmd_line)
1159     {
1160     int i, cpuid = -1, coprocnr = -1;
1161     int gprs, coprocs;
1162     char *p;
1163    
1164     /* [cpuid][,c] */
1165     if (cmd_line[0] != '\0') {
1166     if (cmd_line[0] != ',') {
1167     cpuid = strtoull(cmd_line, NULL, 0);
1168     if (cpuid < 0 || cpuid >= m->ncpus) {
1169     printf("cpu%i doesn't exist.\n", cpuid);
1170     return;
1171     }
1172     }
1173     p = strchr(cmd_line, ',');
1174     if (p != NULL) {
1175     coprocnr = atoi(p + 1);
1176     if (coprocnr < 0 || coprocnr >= 4) {
1177     printf("Invalid coprocessor number.\n");
1178     return;
1179     }
1180     }
1181     }
1182    
1183     gprs = (coprocnr == -1)? 1 : 0;
1184     coprocs = (coprocnr == -1)? 0x0 : (1 << coprocnr);
1185    
1186     for (i=0; i<m->ncpus; i++)
1187     if (cpuid == -1 || i == cpuid)
1188     cpu_register_dump(m, m->cpus[i], gprs, coprocs);
1189     }
1190    
1191    
1192     /*
1193     * debugger_cmd_step():
1194     */
1195     static void debugger_cmd_step(struct machine *m, char *cmd_line)
1196     {
1197     int n = 1;
1198    
1199     if (cmd_line[0] != '\0') {
1200     n = strtoull(cmd_line, NULL, 0);
1201     if (n < 1) {
1202     printf("invalid nr of steps\n");
1203     return;
1204     }
1205     }
1206    
1207     n_steps_left_before_interaction = n - 1;
1208    
1209     /* Special hack, see debugger() for more info. */
1210     exit_debugger = -1;
1211    
1212     strcpy(repeat_cmd, "step");
1213     }
1214    
1215    
1216     /*
1217     * debugger_cmd_tlbdump():
1218     *
1219     * Dump each CPU's TLB contents.
1220     */
1221     static void debugger_cmd_tlbdump(struct machine *m, char *cmd_line)
1222     {
1223     int x = -1;
1224     int rawflag = 0;
1225    
1226     if (cmd_line[0] != '\0') {
1227     char *p;
1228     if (cmd_line[0] != ',') {
1229     x = strtoull(cmd_line, NULL, 0);
1230     if (x < 0 || x >= m->ncpus) {
1231     printf("cpu%i doesn't exist.\n", x);
1232     return;
1233     }
1234     }
1235     p = strchr(cmd_line, ',');
1236     if (p != NULL) {
1237     switch (p[1]) {
1238     case 'r':
1239     case 'R':
1240     rawflag = 1;
1241     break;
1242     default:
1243     printf("Unknown tlbdump flag.\n");
1244     printf("syntax: tlbdump [cpuid][,r]\n");
1245     return;
1246     }
1247     }
1248     }
1249    
1250     cpu_tlbdump(m, x, rawflag);
1251     }
1252    
1253    
1254     /*
1255     * debugger_cmd_trace():
1256     */
1257     static void debugger_cmd_trace(struct machine *m, char *cmd_line)
1258     {
1259     if (*cmd_line) {
1260     printf("syntax: trace\n");
1261     return;
1262     }
1263    
1264     old_show_trace_tree = 1 - old_show_trace_tree;
1265     printf("show_trace_tree = %s\n", old_show_trace_tree? "ON" : "OFF");
1266    
1267     if (m->bintrans_enable && old_show_trace_tree)
1268     printf("NOTE: the trace tree functionality doesn't "
1269     "work very well with bintrans!\n");
1270    
1271     /* TODO: how to preserve quiet_mode? */
1272     old_quiet_mode = 0;
1273     printf("quiet_mode = %s\n", old_quiet_mode? "ON" : "OFF");
1274     }
1275    
1276    
1277     /*
1278     * debugger_cmd_unassemble():
1279     *
1280     * Dump emulated memory as instructions.
1281     *
1282     * syntax: unassemble [addr [endaddr]]
1283     */
1284     static void debugger_cmd_unassemble(struct machine *m, char *cmd_line)
1285     {
1286     uint64_t addr, addr_start, addr_end;
1287     struct cpu *c;
1288     struct memory *mem;
1289     char *p = NULL;
1290     int r, lines_left = -1;
1291    
1292     if (cmd_line[0] != '\0') {
1293     uint64_t tmp;
1294     char *tmps = strdup(cmd_line);
1295    
1296     /* addr: */
1297     p = strchr(tmps, ' ');
1298     if (p != NULL)
1299     *p = '\0';
1300     r = debugger_parse_name(m, tmps, 0, &tmp);
1301     free(tmps);
1302    
1303     if (r == NAME_PARSE_NOMATCH || r == NAME_PARSE_MULTIPLE) {
1304     printf("Unparsable address: %s\n", cmd_line);
1305     return;
1306     } else {
1307     last_unasm_addr = tmp;
1308     }
1309    
1310     p = strchr(cmd_line, ' ');
1311     }
1312    
1313     addr_start = last_unasm_addr;
1314    
1315     if (addr_start == MAGIC_UNTOUCHED) {
1316     uint64_t tmp;
1317     int match_register = 0;
1318     cpu_register_match(m, "pc", 0, &tmp, &match_register);
1319     if (match_register) {
1320     addr_start = tmp;
1321     } else {
1322     printf("No starting address.\n");
1323     return;
1324     }
1325     }
1326    
1327     addr_end = addr_start + 1000;
1328    
1329     /* endaddr: */
1330     if (p != NULL) {
1331     while (*p == ' ' && *p)
1332     p++;
1333     r = debugger_parse_name(m, p, 0, &addr_end);
1334     if (r == NAME_PARSE_NOMATCH || r == NAME_PARSE_MULTIPLE) {
1335     printf("Unparsable address: %s\n", cmd_line);
1336     return;
1337     }
1338     } else
1339     lines_left = 20;
1340    
1341     if (m->cpus == NULL) {
1342     printf("No cpus (?)\n");
1343     return;
1344     }
1345     c = m->cpus[m->bootstrap_cpu];
1346     if (c == NULL) {
1347     printf("m->cpus[m->bootstrap_cpu] = NULL\n");
1348     return;
1349     }
1350     mem = m->cpus[m->bootstrap_cpu]->mem;
1351    
1352     addr = addr_start;
1353    
1354     ctrl_c = 0;
1355    
1356     while (addr < addr_end) {
1357     int i, len;
1358 dpavlin 4 unsigned char buf[32]; /* TODO: How long can an
1359 dpavlin 2 instruction be, on weird archs? */
1360     memset(buf, 0, sizeof(buf));
1361    
1362     for (i=0; i<sizeof(buf); i++)
1363     c->memory_rw(c, mem, addr+i, buf+i, 1, MEM_READ,
1364     CACHE_NONE | NO_EXCEPTIONS);
1365    
1366     len = cpu_disassemble_instr(m, c, buf, 0, addr, 0);
1367    
1368     if (ctrl_c)
1369     return;
1370     if (len == 0)
1371     break;
1372    
1373     addr += len;
1374    
1375     if (lines_left != -1) {
1376     lines_left --;
1377     if (lines_left == 0)
1378     break;
1379     }
1380     }
1381    
1382     last_unasm_addr = addr;
1383    
1384     strcpy(repeat_cmd, "unassemble");
1385     }
1386    
1387    
1388     /*
1389     * debugger_cmd_version():
1390     */
1391     static void debugger_cmd_version(struct machine *m, char *cmd_line)
1392     {
1393     if (*cmd_line) {
1394     printf("syntax: version\n");
1395     return;
1396     }
1397    
1398     #ifdef VERSION
1399     printf("%s, %s\n", VERSION, COMPILE_DATE);
1400     #else
1401     printf("(no version), %s\n", COMPILE_DATE);
1402     #endif
1403     }
1404    
1405    
1406     struct cmd {
1407     char *name;
1408     char *args;
1409     int tmp_flag;
1410     void (*f)(struct machine *, char *cmd_line);
1411     char *description;
1412     };
1413    
1414     static struct cmd cmds[] = {
1415     { "breakpoint", "...", 0, debugger_cmd_breakpoint,
1416     "manipulate breakpoints" },
1417    
1418     { "bintrans", "[on|off]", 0, debugger_cmd_bintrans,
1419     "toggle bintrans on or off" },
1420    
1421     /* NOTE: Try to keep 'c' down to only one command. Having 'continue'
1422     available as a one-letter command is very convenient. */
1423    
1424     { "continue", "", 0, debugger_cmd_continue,
1425     "continue execution" },
1426    
1427     { "device", "...", 0, debugger_cmd_device,
1428     "show info about (or manipulate) devices" },
1429    
1430     { "dump", "[addr [endaddr]]", 0, debugger_cmd_dump,
1431     "dump memory contents in hex and ASCII" },
1432    
1433     { "emuls", "", 0, debugger_cmd_emuls,
1434     "print a summary of all current emuls" },
1435    
1436     { "focus", "x[,y]", 0, debugger_cmd_focus,
1437     "changes focus to machine x (in emul y)" },
1438    
1439     { "help", "", 0, debugger_cmd_help,
1440     "print this help message" },
1441    
1442     { "itrace", "", 0, debugger_cmd_itrace,
1443     "toggle instruction_trace on or off" },
1444    
1445     { "lookup", "name|addr", 0, debugger_cmd_lookup,
1446     "lookup a symbol by name or address" },
1447    
1448     { "machine", "", 0, debugger_cmd_machine,
1449     "print a summary of the current machine" },
1450    
1451     { "opcodestats", "", 0, debugger_cmd_opcodestats,
1452     "show opcode statistics" },
1453    
1454     { "pause", "cpuid", 0, debugger_cmd_pause,
1455     "pause (or unpause) a CPU" },
1456    
1457     { "print", "expr", 0, debugger_cmd_print,
1458     "evaluate an expression without side-effects" },
1459    
1460     { "put", "[b|h|w|d|q] addr, data", 0, debugger_cmd_put,
1461     "modify emulated memory contents" },
1462    
1463     { "quiet", "[on|off]", 0, debugger_cmd_quiet,
1464     "toggle quiet_mode on or off" },
1465    
1466     { "quit", "", 0, debugger_cmd_quit,
1467     "quit the emulator" },
1468    
1469     { "reg", "[cpuid][,c]", 0, debugger_cmd_reg,
1470     "show GPRs (or coprocessor c's registers)" },
1471    
1472     /* NOTE: Try to keep 's' down to only one command. Having 'step'
1473     available as a one-letter command is very convenient. */
1474    
1475     { "step", "[n]", 0, debugger_cmd_step,
1476     "single-step one (or n) instruction(s)" },
1477    
1478     { "tlbdump", "[cpuid][,r]", 0, debugger_cmd_tlbdump,
1479     "dump TLB contents (add ',r' for raw data)" },
1480    
1481     { "trace", "", 0, debugger_cmd_trace,
1482     "toggle show_trace_tree on or off" },
1483    
1484     { "unassemble", "[addr [endaddr]]", 0, debugger_cmd_unassemble,
1485     "dump memory contents as instructions" },
1486    
1487     { "version", "", 0, debugger_cmd_version,
1488     "print version information" },
1489    
1490     { NULL, NULL, 0, NULL, NULL }
1491     };
1492    
1493    
1494     /*
1495     * debugger_cmd_help():
1496     *
1497     * Print a list of available commands.
1498     *
1499     * NOTE: This is placed after the cmds[] array, because it needs to
1500     * access it.
1501     */
1502     static void debugger_cmd_help(struct machine *m, char *cmd_line)
1503     {
1504     int i, j, max_name_len = 0;
1505    
1506     i = 0;
1507     while (cmds[i].name != NULL) {
1508     int a = strlen(cmds[i].name);
1509     if (cmds[i].args != NULL)
1510     a += 1 + strlen(cmds[i].args);
1511     if (a > max_name_len)
1512     max_name_len = a;
1513     i++;
1514     }
1515    
1516     printf("Available commands:\n");
1517    
1518     i = 0;
1519     while (cmds[i].name != NULL) {
1520     char buf[100];
1521     snprintf(buf, sizeof(buf), "%s", cmds[i].name);
1522     if (cmds[i].args != NULL)
1523     snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
1524     " %s", cmds[i].args);
1525    
1526     printf(" ");
1527     for (j=0; j<max_name_len; j++)
1528     if (j < strlen(buf))
1529     printf("%c", buf[j]);
1530     else
1531     printf(" ");
1532    
1533     printf(" %s\n", cmds[i].description);
1534     i++;
1535     }
1536    
1537     printf("Generic assignments: x = expr\n");
1538     printf("where x must be a register, and expr can be a register, a "
1539     "numeric value, or\na symbol name (+ an optional numeric offset)."
1540     " In case there are multiple\nmatches (ie a symbol that has the "
1541     "same name as a register), you may add a\nprefix character as a "
1542     "hint: '%%' for registers, '@' for symbols, and\n'$' for numeric"
1543     " values. Use 0x for hexadecimal values.\n");
1544     }
1545    
1546    
1547     /****************************************************************************/
1548    
1549    
1550     /*
1551     * debugger_assignment():
1552     *
1553     * cmd contains something like "pc=0x80001000", or "r31=memcpy+0x40".
1554     */
1555     void debugger_assignment(struct machine *m, char *cmd)
1556     {
1557     char *left, *right;
1558     int res_left, res_right;
1559     uint64_t tmp;
1560    
1561     left = malloc(strlen(cmd) + 1);
1562     if (left == NULL) {
1563     fprintf(stderr, "out of memory in debugger_assignment()\n");
1564     exit(1);
1565     }
1566     strcpy(left, cmd);
1567     right = strchr(left, '=');
1568     if (right == NULL) {
1569     fprintf(stderr, "internal error in the debugger\n");
1570     exit(1);
1571     }
1572     *right = '\0';
1573    
1574     /* Remove trailing spaces in left: */
1575     while (strlen(left) >= 1 && left[strlen(left)-1] == ' ')
1576     left[strlen(left)-1] = '\0';
1577    
1578     /* Remove leading spaces in right: */
1579     right++;
1580     while (*right == ' ' && *right != '\0')
1581     right++;
1582    
1583     /* printf("left = '%s'\nright = '%s'\n", left, right); */
1584    
1585     res_right = debugger_parse_name(m, right, 0, &tmp);
1586     switch (res_right) {
1587     case NAME_PARSE_NOMATCH:
1588     printf("No match for the right-hand side of the assignment.\n");
1589     break;
1590     case NAME_PARSE_MULTIPLE:
1591     printf("Multiple matches for the right-hand side of the "
1592     "assignment.\n");
1593     break;
1594     default:
1595     res_left = debugger_parse_name(m, left, 1, &tmp);
1596     switch (res_left) {
1597     case NAME_PARSE_NOMATCH:
1598     printf("No match for the left-hand side of the "
1599     "assignment.\n");
1600     break;
1601     case NAME_PARSE_MULTIPLE:
1602     printf("Multiple matches for the left-hand side "
1603     "of the assignment.\n");
1604     break;
1605     default:
1606     debugger_cmd_print(m, left);
1607     }
1608     }
1609    
1610     free(left);
1611     }
1612    
1613    
1614     /*
1615     * debugger_readline():
1616     *
1617     * Read a line from the terminal.
1618     */
1619     static char *debugger_readline(void)
1620     {
1621     int ch, i, j, n, i_match, reallen, cmd_len, cursor_pos;
1622     int read_from_index = last_cmd_index;
1623     char *cmd = last_cmd[last_cmd_index];
1624    
1625     cmd_len = 0; cmd[0] = '\0';
1626     printf("GXemul> ");
1627     fflush(stdout);
1628    
1629     ch = '\0';
1630     cmd_len = 0;
1631     cursor_pos = 0;
1632    
1633     while (ch != '\n') {
1634     /*
1635     * TODO: This uses up 100% CPU, maybe that isn't too good.
1636     * The usleep() call might make it a tiny bit nicer on other
1637     * running processes, but it is still very ugly.
1638     */
1639     while ((ch = console_readchar(MAIN_CONSOLE)) < 0) {
1640     x11_check_event(debugger_emuls, debugger_n_emuls);
1641     usleep(2);
1642     }
1643    
1644     if ((ch == '\b' || ch == 127) && cursor_pos > 0) {
1645     /* Backspace. */
1646     cursor_pos --;
1647     cmd_len --;
1648     memmove(cmd + cursor_pos, cmd + cursor_pos + 1,
1649     cmd_len);
1650     cmd[cmd_len] = '\0';
1651     printf("\b");
1652     for (i=cursor_pos; i<cmd_len; i++)
1653     printf("%c", cmd[i]);
1654     printf(" \b");
1655     for (i=cursor_pos; i<cmd_len; i++)
1656     printf("\b");
1657     } else if (ch == 4 && cmd_len > 0 && cursor_pos < cmd_len) {
1658     /* CTRL-D: Delete. */
1659     cmd_len --;
1660     memmove(cmd + cursor_pos, cmd + cursor_pos + 1,
1661     cmd_len);
1662     cmd[cmd_len] = '\0';
1663     for (i=cursor_pos; i<cmd_len; i++)
1664     printf("%c", cmd[i]);
1665     printf(" \b");
1666     for (i=cursor_pos; i<cmd_len; i++)
1667     printf("\b");
1668     } else if (ch == 1) {
1669     /* CTRL-A: Start of line. */
1670     while (cursor_pos > 0) {
1671     cursor_pos --;
1672     printf("\b");
1673     }
1674     } else if (ch == 2) {
1675     /* CTRL-B: Backwards one character. */
1676     if (cursor_pos > 0) {
1677     printf("\b");
1678     cursor_pos --;
1679     }
1680     } else if (ch == 5) {
1681     /* CTRL-E: End of line. */
1682     while (cursor_pos < cmd_len) {
1683     printf("%c", cmd[cursor_pos]);
1684     cursor_pos ++;
1685     }
1686     } else if (ch == 6) {
1687     /* CTRL-F: Forward one character. */
1688     if (cursor_pos < cmd_len) {
1689     printf("%c",
1690     cmd[cursor_pos]);
1691     cursor_pos ++;
1692     }
1693     } else if (ch == 11) {
1694     /* CTRL-K: Kill to end of line. */
1695     for (i=0; i<MAX_CMD_LEN; i++)
1696     console_makeavail(MAIN_CONSOLE, 4); /* :-) */
1697     } else if (ch == 14 || ch == 16) {
1698     /* CTRL-P: Previous line in the command history,
1699     CTRL-N: next line */
1700     do {
1701     if (ch == 14 &&
1702     read_from_index == last_cmd_index)
1703     break;
1704     if (ch == 16)
1705     i = read_from_index - 1;
1706     else
1707     i = read_from_index + 1;
1708    
1709     if (i < 0)
1710     i = N_PREVIOUS_CMDS - 1;
1711     if (i >= N_PREVIOUS_CMDS)
1712     i = 0;
1713    
1714     /* Special case: pressing 'down'
1715     to reach last_cmd_index: */
1716     if (i == last_cmd_index) {
1717     read_from_index = i;
1718     for (i=cursor_pos; i<cmd_len;
1719     i++)
1720     printf(" ");
1721     for (i=cmd_len-1; i>=0; i--)
1722     printf("\b \b");
1723     cmd[0] = '\0';
1724     cmd_len = cursor_pos = 0;
1725     } else if (last_cmd[i][0] != '\0') {
1726     /* Copy from old line: */
1727     read_from_index = i;
1728     for (i=cursor_pos; i<cmd_len;
1729     i++)
1730     printf(" ");
1731     for (i=cmd_len-1; i>=0; i--)
1732     printf("\b \b");
1733     strcpy(cmd,
1734     last_cmd[read_from_index]);
1735     cmd_len = strlen(cmd);
1736     printf("%s", cmd);
1737     cursor_pos = cmd_len;
1738     }
1739     } while (0);
1740     } else if (ch >= ' ' && cmd_len < MAX_CMD_LEN) {
1741     /* Visible character: */
1742     memmove(cmd + cursor_pos + 1, cmd + cursor_pos,
1743     cmd_len - cursor_pos);
1744     cmd[cursor_pos] = ch;
1745     cmd_len ++;
1746     cursor_pos ++;
1747     cmd[cmd_len] = '\0';
1748     printf("%c", ch);
1749     for (i=cursor_pos; i<cmd_len; i++)
1750     printf("%c", cmd[i]);
1751     for (i=cursor_pos; i<cmd_len; i++)
1752     printf("\b");
1753     } else if (ch == '\r' || ch == '\n') {
1754     ch = '\n';
1755     printf("\n");
1756     } else if (ch == '\t') {
1757     /* Super-simple tab-completion: */
1758     i = 0;
1759     while (cmds[i].name != NULL)
1760     cmds[i++].tmp_flag = 0;
1761    
1762     /* Check for a (partial) command match: */
1763     n = i = i_match = 0;
1764     while (cmds[i].name != NULL) {
1765     if (strncasecmp(cmds[i].name, cmd,
1766     cmd_len) == 0) {
1767     cmds[i].tmp_flag = 1;
1768     i_match = i;
1769     n++;
1770     }
1771     i++;
1772     }
1773    
1774     switch (n) {
1775     case 0: /* Beep. */
1776     printf("\a");
1777     break;
1778     case 1: /* Add the rest of the command: */
1779     reallen = strlen(cmds[i_match].name);
1780     for (i=cmd_len; i<reallen; i++)
1781     console_makeavail(MAIN_CONSOLE,
1782     cmds[i_match].name[i]);
1783     /* ... and a space, if the command takes
1784     any arguments: */
1785     if (cmds[i_match].args != NULL &&
1786     cmds[i_match].args[0] != '\0')
1787     console_makeavail(MAIN_CONSOLE, ' ');
1788     break;
1789     default:
1790     /* Show all possible commands: */
1791     printf("\a\n"); /* Beep. :-) */
1792     i = 0; /* i = cmds index */
1793     j = 0; /* j = # of cmds printed */
1794     while (cmds[i].name != NULL) {
1795     if (cmds[i].tmp_flag) {
1796     int q;
1797     if (j == 0)
1798     printf(" ");
1799     printf("%s",
1800     cmds[i].name);
1801     j++;
1802     if (j != 6)
1803     for (q=0; q<13-strlen(
1804     cmds[i].name); q++)
1805     printf(" ");
1806     if (j == 6) {
1807     printf("\n");
1808     j = 0;
1809     }
1810     }
1811     i++;
1812     }
1813     if (j != 0)
1814     printf("\n");
1815     printf("GXemul> ");
1816     for (i=0; i<cmd_len; i++)
1817     printf("%c", cmd[i]);
1818     }
1819     } else if (ch == 27) {
1820     /* Escape codes: (cursor keys etc) */
1821     while ((ch = console_readchar(MAIN_CONSOLE)) < 0)
1822     usleep(1);
1823     if (ch == '[' || ch == 'O') {
1824     while ((ch = console_readchar(MAIN_CONSOLE))
1825     < 0)
1826     usleep(1);
1827     switch (ch) {
1828     case '2': /* 2~ = ins */
1829     case '5': /* 5~ = pgup */
1830     case '6': /* 6~ = pgdn */
1831     /* TODO: Ugly hack, but might work. */
1832     while ((ch = console_readchar(
1833     MAIN_CONSOLE)) < 0)
1834     usleep(1);
1835     /* Do nothing for these keys. */
1836     break;
1837     case '3': /* 3~ = delete */
1838     /* TODO: Ugly hack, but might work. */
1839     while ((ch = console_readchar(
1840     MAIN_CONSOLE)) < 0)
1841     usleep(1);
1842     console_makeavail(MAIN_CONSOLE, '\b');
1843     break;
1844     case 'A': /* Up. */
1845     /* Up cursor ==> CTRL-P */
1846     console_makeavail(MAIN_CONSOLE, 16);
1847     break;
1848     case 'B': /* Down. */
1849     /* Down cursor ==> CTRL-N */
1850     console_makeavail(MAIN_CONSOLE, 14);
1851     break;
1852     case 'C':
1853     /* Right cursor ==> CTRL-F */
1854     console_makeavail(MAIN_CONSOLE, 6);
1855     break;
1856     case 'D': /* Left */
1857     /* Left cursor ==> CTRL-B */
1858     console_makeavail(MAIN_CONSOLE, 2);
1859     break;
1860     case 'F':
1861     /* End ==> CTRL-E */
1862     console_makeavail(MAIN_CONSOLE, 5);
1863     break;
1864     case 'H':
1865     /* Home ==> CTRL-A */
1866     console_makeavail(MAIN_CONSOLE, 1);
1867     break;
1868     }
1869     }
1870     }
1871    
1872     fflush(stdout);
1873     }
1874    
1875     return cmd;
1876     }
1877    
1878    
1879     /*
1880     * debugger():
1881     *
1882     * This is a loop, which reads a command from the terminal, and executes it.
1883     */
1884     void debugger(void)
1885     {
1886     int i, n, i_match, matchlen, cmd_len;
1887     char *cmd;
1888    
1889     if (n_steps_left_before_interaction > 0) {
1890     n_steps_left_before_interaction --;
1891     return;
1892     }
1893    
1894     exit_debugger = 0;
1895    
1896     while (!exit_debugger) {
1897     /* Read a line from the terminal: */
1898     cmd = debugger_readline();
1899     cmd_len = strlen(cmd);
1900    
1901     /* Remove spaces: */
1902     while (cmd_len > 0 && cmd[0]==' ')
1903     memmove(cmd, cmd+1, cmd_len --);
1904     while (cmd_len > 0 && cmd[cmd_len-1] == ' ')
1905     cmd[(cmd_len--)-1] = '\0';
1906    
1907     /* No command? Then try reading another line. */
1908     if (cmd_len == 0) {
1909     /* Special case for repeated commands: */
1910     if (repeat_cmd[0] != '\0')
1911     strcpy(cmd, repeat_cmd);
1912     else
1913     continue;
1914     } else {
1915     last_cmd_index ++;
1916     if (last_cmd_index >= N_PREVIOUS_CMDS)
1917     last_cmd_index = 0;
1918    
1919     repeat_cmd[0] = '\0';
1920     }
1921    
1922     /*
1923     * Is there a '=' on the command line? Then try to do an
1924     * assignment. (Only if there is just one word, followed
1925     * by the '=' sign. This makes it possible to use commands
1926     * such as "device add name addr=xyz".)
1927     */
1928     if (strchr(cmd, '=') != NULL) {
1929     /* Count the nr of words: */
1930     int nw = 0, inword = 0;
1931     char *p = cmd;
1932     while (*p) {
1933     if (*p == '=')
1934     break;
1935     if (*p != ' ') {
1936     if (!inword)
1937     nw ++;
1938     inword = 1;
1939     } else
1940     inword = 0;
1941     p++;
1942     }
1943    
1944     if (nw == 1) {
1945     debugger_assignment(debugger_machine, cmd);
1946     continue;
1947     }
1948     }
1949    
1950     i = 0;
1951     while (cmds[i].name != NULL)
1952     cmds[i++].tmp_flag = 0;
1953    
1954     /* How many chars in cmd to match against: */
1955     matchlen = 0;
1956     while (isalpha((int)cmd[matchlen]))
1957     matchlen ++;
1958    
1959     /* Check for a command name match: */
1960     n = i = i_match = 0;
1961     while (cmds[i].name != NULL) {
1962     if (strncasecmp(cmds[i].name, cmd, matchlen) == 0) {
1963     cmds[i].tmp_flag = 1;
1964     i_match = i;
1965     n++;
1966     }
1967     i++;
1968     }
1969    
1970     /* No match? */
1971     if (n == 0) {
1972     printf("Unknown command '%s'. "
1973     "Type 'help' for help.\n", cmd);
1974     continue;
1975     }
1976    
1977     /* More than one match? */
1978     if (n > 1) {
1979     printf("Ambiguous command '%s': ", cmd);
1980     i = 0;
1981     while (cmds[i].name != NULL) {
1982     if (cmds[i].tmp_flag)
1983     printf(" %s", cmds[i].name);
1984     i++;
1985     }
1986     printf("\n");
1987     continue;
1988     }
1989    
1990     /* Exactly one match: */
1991     if (cmds[i_match].f != NULL) {
1992     char *p = cmd + matchlen;
1993     /* Remove leading whitespace from the args... */
1994     while (*p != '\0' && *p == ' ')
1995     p++;
1996    
1997     /* ... and run the command: */
1998     cmds[i_match].f(debugger_machine, p);
1999     } else
2000     printf("FATAL ERROR: internal error in debugger.c:"
2001     " no handler for this command?\n");
2002    
2003     /* Special hack for the "step" command: */
2004     if (exit_debugger == -1)
2005     return;
2006     }
2007    
2008     single_step = 0;
2009     debugger_machine->instruction_trace = old_instruction_trace;
2010     debugger_machine->show_trace_tree = old_show_trace_tree;
2011     quiet_mode = old_quiet_mode;
2012     }
2013    
2014    
2015     /*
2016     * debugger_reset():
2017     *
2018     * This function should be called before calling debugger(), when it is
2019     * absolutely necessary that debugger() is interactive. Otherwise, it might
2020     * return without doing anything, such as when single-stepping multiple
2021     * instructions at a time.
2022     */
2023     void debugger_reset(void)
2024     {
2025     n_steps_left_before_interaction = 0;
2026     }
2027    
2028    
2029     /*
2030     * debugger_init():
2031     *
2032     * Must be called before any other debugger function is used.
2033     */
2034     void debugger_init(struct emul **emuls, int n_emuls)
2035     {
2036     int i;
2037    
2038     debugger_n_emuls = n_emuls;
2039     debugger_emuls = emuls;
2040    
2041     if (n_emuls < 1) {
2042     fprintf(stderr, "\nERROR: No emuls (?)\n");
2043     exit(1);
2044     }
2045    
2046     debugger_emul = emuls[0];
2047     if (emuls[0]->n_machines < 1) {
2048     fprintf(stderr, "\nERROR: No machines in emuls[0], "
2049     "cannot handle this situation yet.\n\n");
2050     exit(1);
2051     }
2052    
2053     debugger_machine = emuls[0]->machines[0];
2054    
2055     for (i=0; i<N_PREVIOUS_CMDS; i++) {
2056     last_cmd[i] = malloc(MAX_CMD_LEN + 1);
2057     if (last_cmd[i] == NULL) {
2058     fprintf(stderr, "debugger_init(): out of memory\n");
2059     exit(1);
2060     }
2061     last_cmd[i][0] = '\0';
2062     }
2063    
2064     last_cmd_index = 0;
2065     repeat_cmd[0] = '\0';
2066     }
2067    

  ViewVC Help
Powered by ViewVC 1.1.26