/[gxemul]/trunk/src/debugger/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/debugger.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 24 - (hide annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 22609 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


1 dpavlin 24 /*
2     * Copyright (C) 2004-2006 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     * $Id: debugger.c,v 1.10 2006/06/16 18:31:26 debug Exp $
29     *
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     * Call stack display (back-trace)?
40     *
41     * More generic expression evaluator (for example + - * / between multiple
42     * terms), including _TAB COMPLETION_ of symbols and register names!
43     * Must be possible to reach any emul, any machine in any emul, any
44     * cpu in any machine, other variables in the emulator, and so forth.
45     * Perhaps a "settable variables registry" somewhere?
46     *
47     * Nicer looking output of register dumps, floating point registers,
48     * etc. Warn about weird/invalid register contents.
49     *
50     * Ctrl-C doesn't enter the debugger on some OSes (HP-UX?)...
51     *
52     * Many other TODOs.
53     */
54    
55     #include <ctype.h>
56     #include <signal.h>
57     #include <stdio.h>
58     #include <stdlib.h>
59     #include <string.h>
60     #include <unistd.h>
61    
62     #include "console.h"
63     #include "cpu.h"
64     #include "device.h"
65     #include "debugger.h"
66     #include "debugger_gdb.h"
67     #include "diskimage.h"
68     #include "emul.h"
69     #include "machine.h"
70     #include "memory.h"
71     #include "misc.h"
72     #include "net.h"
73     #include "settings.h"
74     #include "x11.h"
75    
76    
77     extern int extra_argc;
78     extern char **extra_argv;
79     extern struct settings *global_settings;
80     extern int quiet_mode;
81    
82    
83     /*
84     * Global debugger variables:
85     *
86     * TODO: Some of these should be moved to some other place!
87     */
88    
89     volatile int single_step = 0;
90     volatile int exit_debugger;
91     int force_debugger_at_exit = 0;
92     int show_opcode_statistics = 0;
93    
94     volatile int single_step_breakpoint = 0;
95     int debugger_n_steps_left_before_interaction = 0;
96    
97     int old_instruction_trace = 0;
98     int old_quiet_mode = 0;
99     int old_show_trace_tree = 0;
100    
101    
102     /*
103     * Private (global) debugger variables:
104     */
105    
106     static volatile int ctrl_c;
107    
108     static int debugger_n_emuls;
109     static struct emul **debugger_emuls;
110     static struct emul *debugger_emul;
111     static struct machine *debugger_machine;
112    
113     #define MAX_CMD_BUFLEN 72
114     #define N_PREVIOUS_CMDS 150
115     static char *last_cmd[N_PREVIOUS_CMDS];
116     static int last_cmd_index;
117    
118     static char repeat_cmd[MAX_CMD_BUFLEN];
119    
120     #define MAGIC_UNTOUCHED 0x98ca76c2ffcc0011ULL
121    
122     static uint64_t last_dump_addr = MAGIC_UNTOUCHED;
123     static uint64_t last_unasm_addr = MAGIC_UNTOUCHED;
124    
125    
126     /*
127     * debugger_readchar():
128     */
129     char debugger_readchar(void)
130     {
131     int ch, i, j;
132    
133     while ((ch = console_readchar(MAIN_CONSOLE)) < 0 && !exit_debugger) {
134     /* Check for X11 events: */
135     x11_check_event(debugger_emuls, debugger_n_emuls);
136    
137     /* Check for incoming GDB packets: */
138     for (i=0; i<debugger_n_emuls; i++) {
139     struct emul *e = debugger_emuls[i];
140     if (e == NULL)
141     continue;
142    
143     for (j=0; j<e->n_machines; j++) {
144     if (e->machines[j]->gdb.port > 0)
145     debugger_gdb_check_incoming(
146     e->machines[j]);
147     }
148     }
149    
150     /* TODO: The X11 and GDB checks above should probably
151     be factored out... */
152    
153     /* Give up some CPU time: */
154     usleep(1);
155     }
156     return ch;
157     }
158    
159    
160     /*
161     * debugger_activate():
162     *
163     * This is a signal handler for CTRL-C. It shouldn't be called directly,
164     * but setup code in emul.c sets the CTRL-C signal handler to use this
165     * function.
166     */
167     void debugger_activate(int x)
168     {
169     ctrl_c = 1;
170    
171     if (single_step) {
172     /* Already in the debugger. Do nothing. */
173     int i;
174     for (i=0; i<MAX_CMD_BUFLEN; i++)
175     console_makeavail(MAIN_CONSOLE, '\b');
176     console_makeavail(MAIN_CONSOLE, ' ');
177     console_makeavail(MAIN_CONSOLE, '\n');
178     printf("^C");
179     fflush(stdout);
180     } else {
181     /* Enter the single step debugger. */
182     single_step = 1;
183    
184     /* Discard any chars in the input queue: */
185     while (console_charavail(MAIN_CONSOLE))
186     console_readchar(MAIN_CONSOLE);
187     }
188    
189     /* Clear the repeat-command buffer: */
190     repeat_cmd[0] = '\0';
191    
192     /* Reactivate the signal handler: */
193     signal(SIGINT, debugger_activate);
194     }
195    
196    
197     /*
198     * debugger_parse_name():
199     *
200     * This function reads a string, and tries to match it to a register name,
201     * a symbol, or treat it as a decimal numeric value.
202     *
203     * Some examples:
204     *
205     * "0x7fff1234" ==> numeric value (hex, in this case)
206     * "pc", "r5", "hi", "t4" ==> register (CPU dependent)
207     * "memcpy+64" ==> symbol (plus offset)
208     *
209     * Register names can be preceeded by "x:" where x is the CPU number. (CPU
210     * 0 is assumed by default.)
211     *
212     * To force detection of different types, a character can be added in front of
213     * the name: "$" for numeric values, "%" for registers, and "@" for symbols.
214     *
215     * Return value is:
216     *
217     * NAME_PARSE_NOMATCH no match
218     * NAME_PARSE_MULTIPLE multiple matches
219     *
220     * or one of these (and then *valuep is read or written, depending on
221     * the writeflag):
222     *
223     * NAME_PARSE_REGISTER a register
224     * NAME_PARSE_NUMBER a hex number
225     * NAME_PARSE_SYMBOL a symbol
226     */
227     #define NAME_PARSE_NOMATCH 0
228     #define NAME_PARSE_MULTIPLE 1
229     #define NAME_PARSE_REGISTER 2
230     #define NAME_PARSE_NUMBER 3
231     #define NAME_PARSE_SYMBOL 4
232     static int debugger_parse_name(struct machine *m, char *name, int writeflag,
233     uint64_t *valuep)
234     {
235     int match_register = 0, match_symbol = 0, match_numeric = 0;
236     int skip_register, skip_numeric, skip_symbol;
237    
238     if (m == NULL || name == NULL) {
239     fprintf(stderr, "debugger_parse_name(): NULL ptr\n");
240     exit(1);
241     }
242    
243     /* Warn about non-signextended values: */
244     if (writeflag) {
245     if (m->cpus[0]->is_32bit) {
246     /* Automagically sign-extend. TODO: Is this good? */
247     if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL)
248     (*valuep) |= 0xffffffff00000000ULL;
249     } else {
250     if (((*valuep) >> 32) == 0 && (*valuep) & 0x80000000ULL)
251     printf("WARNING: The value is not sign-extende"
252     "d. Is this what you intended?\n");
253     }
254     }
255    
256     skip_register = name[0] == '$' || name[0] == '@';
257     skip_numeric = name[0] == '%' || name[0] == '@';
258     skip_symbol = name[0] == '$' || name[0] == '%';
259    
260     /* Check for a register match: */
261     if (!skip_register && strlen(name) >= 1)
262     cpu_register_match(m, name, writeflag, valuep,
263     &match_register);
264    
265     /* Check for a number match: */
266     if (!skip_numeric && isdigit((int)name[0])) {
267     uint64_t x;
268     x = strtoull(name, NULL, 0);
269     if (writeflag) {
270     printf("You cannot assign like that.\n");
271     } else
272     *valuep = x;
273     match_numeric = 1;
274     }
275    
276     /* Check for a symbol match: */
277     if (!skip_symbol) {
278     int res;
279     char *p, *sn;
280     uint64_t newaddr, ofs = 0;
281    
282     sn = malloc(strlen(name) + 1);
283     if (sn == NULL) {
284     fprintf(stderr, "out of memory in debugger\n");
285     exit(1);
286     }
287     strlcpy(sn, name, strlen(name)+1);
288    
289     /* Is there a '+' in there? Then treat that as an offset: */
290     p = strchr(sn, '+');
291     if (p != NULL) {
292     *p = '\0';
293     ofs = strtoull(p+1, NULL, 0);
294     }
295    
296     res = get_symbol_addr(&m->symbol_context, sn, &newaddr);
297     if (res) {
298     if (writeflag) {
299     printf("You cannot assign like that.\n");
300     } else
301     *valuep = newaddr + ofs;
302     match_symbol = 1;
303     }
304    
305     free(sn);
306     }
307    
308     if (match_register + match_symbol + match_numeric > 1)
309     return NAME_PARSE_MULTIPLE;
310    
311     if (match_register)
312     return NAME_PARSE_REGISTER;
313     if (match_numeric)
314     return NAME_PARSE_NUMBER;
315     if (match_symbol)
316     return NAME_PARSE_SYMBOL;
317    
318     return NAME_PARSE_NOMATCH;
319     }
320    
321    
322     /*
323     * show_breakpoint():
324     */
325     static void show_breakpoint(struct machine *m, int i)
326     {
327     printf("%3i: 0x", i);
328     if (m->cpus[0]->is_32bit)
329     printf("%08"PRIx32, (uint32_t) m->breakpoint_addr[i]);
330     else
331     printf("%016"PRIx64, (uint64_t) m->breakpoint_addr[i]);
332     if (m->breakpoint_string[i] != NULL)
333     printf(" (%s)", m->breakpoint_string[i]);
334     if (m->breakpoint_flags[i])
335     printf(": flags=0x%x", m->breakpoint_flags[i]);
336     printf("\n");
337     }
338    
339    
340     /****************************************************************************/
341    
342    
343     #include "debugger_cmds.c"
344    
345    
346     /****************************************************************************/
347    
348    
349     /*
350     * debugger_assignment():
351     *
352     * cmd contains something like "pc=0x80001000", or "r31=memcpy+0x40".
353     */
354     void debugger_assignment(struct machine *m, char *cmd)
355     {
356     char *left, *right;
357     int res_left, res_right;
358     uint64_t tmp;
359     uint64_t old_pc = m->cpus[0]->pc; /* TODO: multiple cpus? */
360    
361     left = malloc(MAX_CMD_BUFLEN);
362     if (left == NULL) {
363     fprintf(stderr, "out of memory in debugger_assignment()\n");
364     exit(1);
365     }
366     strlcpy(left, cmd, MAX_CMD_BUFLEN);
367     right = strchr(left, '=');
368     if (right == NULL) {
369     fprintf(stderr, "internal error in the debugger\n");
370     exit(1);
371     }
372     *right = '\0';
373    
374     /* Remove trailing spaces in left: */
375     while (strlen(left) >= 1 && left[strlen(left)-1] == ' ')
376     left[strlen(left)-1] = '\0';
377    
378     /* Remove leading spaces in right: */
379     right++;
380     while (*right == ' ' && *right != '\0')
381     right++;
382    
383     /* printf("left = '%s'\nright = '%s'\n", left, right); */
384    
385     res_right = debugger_parse_name(m, right, 0, &tmp);
386     switch (res_right) {
387     case NAME_PARSE_NOMATCH:
388     printf("No match for the right-hand side of the assignment.\n");
389     break;
390     case NAME_PARSE_MULTIPLE:
391     printf("Multiple matches for the right-hand side of the "
392     "assignment.\n");
393     break;
394     default:
395     res_left = debugger_parse_name(m, left, 1, &tmp);
396     switch (res_left) {
397     case NAME_PARSE_NOMATCH:
398     printf("No match for the left-hand side of the "
399     "assignment.\n");
400     break;
401     case NAME_PARSE_MULTIPLE:
402     printf("Multiple matches for the left-hand side "
403     "of the assignment.\n");
404     break;
405     default:
406     debugger_cmd_print(m, left);
407     }
408     }
409    
410     /*
411     * If the PC has changed, then release any breakpoint we were
412     * currently stopped at.
413     *
414     * TODO: multiple cpus?
415     */
416     if (old_pc != m->cpus[0]->pc)
417     single_step_breakpoint = 0;
418    
419     free(left);
420     }
421    
422    
423     /*
424     * debugger_execute_cmd():
425     */
426     void debugger_execute_cmd(char *cmd, int cmd_len)
427     {
428     int i, n, i_match, matchlen;
429    
430     /*
431     * Is there a '=' on the command line? Then try to do an
432     * assignment. (Only if there is just one word, followed
433     * by the '=' sign. This makes it possible to use commands
434     * such as "device add name addr=xyz".)
435     */
436     if (strchr(cmd, '=') != NULL) {
437     /* Count the nr of words: */
438     int nw = 0, inword = 0;
439     char *p = cmd;
440     while (*p) {
441     if (*p == '=')
442     break;
443     if (*p != ' ') {
444     if (!inword)
445     nw ++;
446     inword = 1;
447     } else
448     inword = 0;
449     p++;
450     }
451    
452     if (nw == 1) {
453     debugger_assignment(debugger_machine, cmd);
454     return;
455     }
456     }
457    
458     i = 0;
459     while (cmds[i].name != NULL)
460     cmds[i++].tmp_flag = 0;
461    
462     /* How many chars in cmd to match against: */
463     matchlen = 0;
464     while (isalpha((int)cmd[matchlen]))
465     matchlen ++;
466    
467     /* Check for a command name match: */
468     n = i = i_match = 0;
469     while (cmds[i].name != NULL) {
470     if (strncasecmp(cmds[i].name, cmd, matchlen) == 0
471     && cmds[i].f != NULL) {
472     cmds[i].tmp_flag = 1;
473     i_match = i;
474     n++;
475     }
476     i++;
477     }
478    
479     /* No match? */
480     if (n == 0) {
481     printf("Unknown command '%s'. Type 'help' for help.\n", cmd);
482     return;
483     }
484    
485     /* More than one match? */
486     if (n > 1) {
487     printf("Ambiguous command '%s': ", cmd);
488     i = 0;
489     while (cmds[i].name != NULL) {
490     if (cmds[i].tmp_flag)
491     printf(" %s", cmds[i].name);
492     i++;
493     }
494     printf("\n");
495     return;
496     }
497    
498     /* Exactly one match: */
499     if (cmds[i_match].f != NULL) {
500     char *p = cmd + matchlen;
501     /* Remove leading whitespace from the args... */
502     while (*p != '\0' && *p == ' ')
503     p++;
504    
505     /* ... and run the command: */
506     cmds[i_match].f(debugger_machine, p);
507     } else
508     printf("FATAL ERROR: internal error in debugger.c:"
509     " no handler for this command?\n");
510     }
511    
512    
513     /*
514     * debugger_readline():
515     *
516     * Read a line from the terminal.
517     */
518     static char *debugger_readline(void)
519     {
520     int ch, i, j, n, i_match, reallen, cmd_len, cursor_pos;
521     int read_from_index = last_cmd_index;
522     char *cmd = last_cmd[last_cmd_index];
523    
524     cmd_len = 0; cmd[0] = '\0';
525     printf("GXemul> ");
526     fflush(stdout);
527    
528     ch = '\0';
529     cmd_len = 0;
530     cursor_pos = 0;
531    
532     while (ch != '\n' && !exit_debugger) {
533     ch = debugger_readchar();
534    
535     if ((ch == '\b' || ch == 127) && cursor_pos > 0) {
536     /* Backspace. */
537     cursor_pos --;
538     cmd_len --;
539     memmove(cmd + cursor_pos, cmd + cursor_pos + 1,
540     cmd_len);
541     cmd[cmd_len] = '\0';
542     printf("\b");
543     for (i=cursor_pos; i<cmd_len; i++)
544     printf("%c", cmd[i]);
545     printf(" \b");
546     for (i=cursor_pos; i<cmd_len; i++)
547     printf("\b");
548     } else if (ch == 4 && cmd_len > 0 && cursor_pos < cmd_len) {
549     /* CTRL-D: Delete. */
550     cmd_len --;
551     memmove(cmd + cursor_pos, cmd + cursor_pos + 1,
552     cmd_len);
553     cmd[cmd_len] = '\0';
554     for (i=cursor_pos; i<cmd_len; i++)
555     printf("%c", cmd[i]);
556     printf(" \b");
557     for (i=cursor_pos; i<cmd_len; i++)
558     printf("\b");
559     } else if (ch == 1) {
560     /* CTRL-A: Start of line. */
561     while (cursor_pos > 0) {
562     cursor_pos --;
563     printf("\b");
564     }
565     } else if (ch == 2) {
566     /* CTRL-B: Backwards one character. */
567     if (cursor_pos > 0) {
568     printf("\b");
569     cursor_pos --;
570     }
571     } else if (ch == 5) {
572     /* CTRL-E: End of line. */
573     while (cursor_pos < cmd_len) {
574     printf("%c", cmd[cursor_pos]);
575     cursor_pos ++;
576     }
577     } else if (ch == 6) {
578     /* CTRL-F: Forward one character. */
579     if (cursor_pos < cmd_len) {
580     printf("%c",
581     cmd[cursor_pos]);
582     cursor_pos ++;
583     }
584     } else if (ch == 11) {
585     /* CTRL-K: Kill to end of line. */
586     for (i=0; i<MAX_CMD_BUFLEN; i++)
587     console_makeavail(MAIN_CONSOLE, 4); /* :-) */
588     } else if (ch == 14 || ch == 16) {
589     /* CTRL-P: Previous line in the command history,
590     CTRL-N: next line */
591     do {
592     if (ch == 14 &&
593     read_from_index == last_cmd_index)
594     break;
595     if (ch == 16)
596     i = read_from_index - 1;
597     else
598     i = read_from_index + 1;
599    
600     if (i < 0)
601     i = N_PREVIOUS_CMDS - 1;
602     if (i >= N_PREVIOUS_CMDS)
603     i = 0;
604    
605     /* Special case: pressing 'down'
606     to reach last_cmd_index: */
607     if (i == last_cmd_index) {
608     read_from_index = i;
609     for (i=cursor_pos; i<cmd_len;
610     i++)
611     printf(" ");
612     for (i=cmd_len-1; i>=0; i--)
613     printf("\b \b");
614     cmd[0] = '\0';
615     cmd_len = cursor_pos = 0;
616     } else if (last_cmd[i][0] != '\0') {
617     /* Copy from old line: */
618     read_from_index = i;
619     for (i=cursor_pos; i<cmd_len;
620     i++)
621     printf(" ");
622     for (i=cmd_len-1; i>=0; i--)
623     printf("\b \b");
624     strlcpy(cmd,
625     last_cmd[read_from_index],
626     MAX_CMD_BUFLEN);
627     cmd_len = strlen(cmd);
628     printf("%s", cmd);
629     cursor_pos = cmd_len;
630     }
631     } while (0);
632     } else if (ch >= ' ' && cmd_len < MAX_CMD_BUFLEN-1) {
633     /* Visible character: */
634     memmove(cmd + cursor_pos + 1, cmd + cursor_pos,
635     cmd_len - cursor_pos);
636     cmd[cursor_pos] = ch;
637     cmd_len ++;
638     cursor_pos ++;
639     cmd[cmd_len] = '\0';
640     printf("%c", ch);
641     for (i=cursor_pos; i<cmd_len; i++)
642     printf("%c", cmd[i]);
643     for (i=cursor_pos; i<cmd_len; i++)
644     printf("\b");
645     } else if (ch == '\r' || ch == '\n') {
646     ch = '\n';
647     printf("\n");
648     } else if (ch == '\t') {
649     /* Super-simple tab-completion: */
650     i = 0;
651     while (cmds[i].name != NULL)
652     cmds[i++].tmp_flag = 0;
653    
654     /* Check for a (partial) command match: */
655     n = i = i_match = 0;
656     while (cmds[i].name != NULL) {
657     if (strncasecmp(cmds[i].name, cmd,
658     cmd_len) == 0) {
659     cmds[i].tmp_flag = 1;
660     i_match = i;
661     n++;
662     }
663     i++;
664     }
665    
666     switch (n) {
667     case 0: /* Beep. */
668     printf("\a");
669     break;
670     case 1: /* Add the rest of the command: */
671     reallen = strlen(cmds[i_match].name);
672     for (i=cmd_len; i<reallen; i++)
673     console_makeavail(MAIN_CONSOLE,
674     cmds[i_match].name[i]);
675     /* ... and a space, if the command takes
676     any arguments: */
677     if (cmds[i_match].args != NULL &&
678     cmds[i_match].args[0] != '\0')
679     console_makeavail(MAIN_CONSOLE, ' ');
680     break;
681     default:
682     /* Show all possible commands: */
683     printf("\a\n"); /* Beep. :-) */
684     i = 0; /* i = cmds index */
685     j = 0; /* j = # of cmds printed */
686     while (cmds[i].name != NULL) {
687     if (cmds[i].tmp_flag) {
688     size_t q;
689     if (j == 0)
690     printf(" ");
691     printf("%s",
692     cmds[i].name);
693     j++;
694     if (j != 6)
695     for (q=0; q<13-strlen(
696     cmds[i].name); q++)
697     printf(" ");
698     if (j == 6) {
699     printf("\n");
700     j = 0;
701     }
702     }
703     i++;
704     }
705     if (j != 0)
706     printf("\n");
707     printf("GXemul> ");
708     for (i=0; i<cmd_len; i++)
709     printf("%c", cmd[i]);
710     }
711     } else if (ch == 27) {
712     /* Escape codes: (cursor keys etc) */
713     while ((ch = console_readchar(MAIN_CONSOLE)) < 0)
714     usleep(1);
715     if (ch == '[' || ch == 'O') {
716     while ((ch = console_readchar(MAIN_CONSOLE))
717     < 0)
718     usleep(1);
719     switch (ch) {
720     case '2': /* 2~ = ins */
721     case '5': /* 5~ = pgup */
722     case '6': /* 6~ = pgdn */
723     /* TODO: Ugly hack, but might work. */
724     while ((ch = console_readchar(
725     MAIN_CONSOLE)) < 0)
726     usleep(1);
727     /* Do nothing for these keys. */
728     break;
729     case '3': /* 3~ = delete */
730     /* TODO: Ugly hack, but might work. */
731     while ((ch = console_readchar(
732     MAIN_CONSOLE)) < 0)
733     usleep(1);
734     console_makeavail(MAIN_CONSOLE, '\b');
735     break;
736     case 'A': /* Up. */
737     /* Up cursor ==> CTRL-P */
738     console_makeavail(MAIN_CONSOLE, 16);
739     break;
740     case 'B': /* Down. */
741     /* Down cursor ==> CTRL-N */
742     console_makeavail(MAIN_CONSOLE, 14);
743     break;
744     case 'C':
745     /* Right cursor ==> CTRL-F */
746     console_makeavail(MAIN_CONSOLE, 6);
747     break;
748     case 'D': /* Left */
749     /* Left cursor ==> CTRL-B */
750     console_makeavail(MAIN_CONSOLE, 2);
751     break;
752     case 'F':
753     /* End ==> CTRL-E */
754     console_makeavail(MAIN_CONSOLE, 5);
755     break;
756     case 'H':
757     /* Home ==> CTRL-A */
758     console_makeavail(MAIN_CONSOLE, 1);
759     break;
760     }
761     }
762     }
763    
764     fflush(stdout);
765     }
766    
767     if (exit_debugger)
768     cmd[0] = '\0';
769    
770     return cmd;
771     }
772    
773    
774     /*
775     * debugger():
776     *
777     * This is a loop, which reads a command from the terminal, and executes it.
778     */
779     void debugger(void)
780     {
781     int i, cmd_len;
782     char *cmd;
783    
784     if (debugger_n_steps_left_before_interaction > 0) {
785     debugger_n_steps_left_before_interaction --;
786     return;
787     }
788    
789     /*
790     * Clear all dyntrans translations, because otherwise things would
791     * become to complex to keep in sync.
792     */
793     /* TODO: In all machines */
794     for (i=0; i<debugger_machine->ncpus; i++)
795     if (debugger_machine->cpus[i]->translation_cache != NULL)
796     cpu_create_or_reset_tc(debugger_machine->cpus[i]);
797    
798     /*
799     * Ugly GDB hack: After single stepping, we need to send back
800     * status to GDB:
801     */
802     if (exit_debugger == -1) {
803     int i, j;
804     for (i=0; i<debugger_n_emuls; i++) {
805     struct emul *e = debugger_emuls[i];
806     if (e == NULL)
807     continue;
808    
809     for (j=0; j<e->n_machines; j++) {
810     if (e->machines[j]->gdb.port > 0)
811     debugger_gdb_after_singlestep(
812     e->machines[j]);
813     }
814     }
815     }
816    
817    
818     exit_debugger = 0;
819    
820     while (!exit_debugger) {
821     /* Read a line from the terminal: */
822     cmd = debugger_readline();
823    
824     /* Special hack for the "step" _GDB_ command: */
825     if (exit_debugger == -1)
826     return;
827    
828     cmd_len = strlen(cmd);
829    
830     /* Remove spaces: */
831     while (cmd_len > 0 && cmd[0]==' ')
832     memmove(cmd, cmd+1, cmd_len --);
833     while (cmd_len > 0 && cmd[cmd_len-1] == ' ')
834     cmd[(cmd_len--)-1] = '\0';
835    
836     /* No command? Then try reading another line. */
837     if (cmd_len == 0) {
838     /* Special case for repeated commands: */
839     if (repeat_cmd[0] != '\0')
840     strlcpy(cmd, repeat_cmd, MAX_CMD_BUFLEN);
841     else
842     continue;
843     } else {
844     last_cmd_index ++;
845     if (last_cmd_index >= N_PREVIOUS_CMDS)
846     last_cmd_index = 0;
847    
848     repeat_cmd[0] = '\0';
849     }
850    
851     debugger_execute_cmd(cmd, cmd_len);
852    
853     /* Special hack for the "step" command: */
854     if (exit_debugger == -1)
855     return;
856     }
857    
858     gettimeofday(&debugger_machine->starttime, NULL);
859     debugger_machine->ncycles_since_gettimeofday = 0;
860    
861     single_step = 0;
862     debugger_machine->instruction_trace = old_instruction_trace;
863     debugger_machine->show_trace_tree = old_show_trace_tree;
864     quiet_mode = old_quiet_mode;
865     }
866    
867    
868     /*
869     * debugger_reset():
870     *
871     * This function should be called before calling debugger(), when it is
872     * absolutely necessary that debugger() is interactive. Otherwise, it might
873     * return without doing anything, such as when single-stepping multiple
874     * instructions at a time.
875     */
876     void debugger_reset(void)
877     {
878     debugger_n_steps_left_before_interaction = 0;
879     }
880    
881    
882     /*
883     * debugger_init():
884     *
885     * Must be called before any other debugger function is used.
886     */
887     void debugger_init(struct emul **emuls, int n_emuls)
888     {
889     int i, j;
890    
891     debugger_n_emuls = n_emuls;
892     debugger_emuls = emuls;
893    
894     if (n_emuls < 1) {
895     fprintf(stderr, "\nERROR: No emuls (?)\n");
896     exit(1);
897     }
898    
899     debugger_emul = emuls[0];
900     if (emuls[0]->n_machines < 1) {
901     fprintf(stderr, "\nERROR: No machines in emuls[0], "
902     "cannot handle this situation yet.\n\n");
903     exit(1);
904     }
905    
906     for (i=0; i<n_emuls; i++)
907     for (j=0; j<emuls[i]->n_machines; j++)
908     debugger_gdb_init(emuls[i]->machines[j]);
909    
910     debugger_machine = emuls[0]->machines[0];
911    
912     for (i=0; i<N_PREVIOUS_CMDS; i++) {
913     last_cmd[i] = malloc(MAX_CMD_BUFLEN);
914     if (last_cmd[i] == NULL) {
915     fprintf(stderr, "debugger_init(): out of memory\n");
916     exit(1);
917     }
918     last_cmd[i][0] = '\0';
919     }
920    
921     last_cmd_index = 0;
922     repeat_cmd[0] = '\0';
923     }
924    

  ViewVC Help
Powered by ViewVC 1.1.26