0.6.7.22: removed CVS dollar-Header-dollar tags from sources
[sbcl.git] / src / runtime / monitor.c
1 /*
2  * This software is part of the SBCL system. See the README file for
3  * more information.
4  *
5  * This software is derived from the CMU CL system, which was
6  * written at Carnegie Mellon University and released into the
7  * public domain. The software is in the public domain and is
8  * provided with absolutely no warranty. See the COPYING and CREDITS
9  * files for more information.
10  */
11
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <stdlib.h>
15 #include <setjmp.h>
16 #include <sys/time.h>
17 #include <sys/resource.h>
18 #include <signal.h>
19
20 #include "runtime.h"
21 #include "sbcl.h"
22 #include "globals.h"
23 #include "vars.h"
24 #include "parse.h"
25 #include "os.h"
26 #include "interrupt.h"
27 #include "lispregs.h"
28 #include "monitor.h"
29 #include "print.h"
30 #include "arch.h"
31 #include "gc.h"
32 #include "search.h"
33 #include "purify.h"
34
35 extern boolean isatty(int fd);
36
37 typedef void cmd(char **ptr);
38
39 static cmd call_cmd, dump_cmd, print_cmd, quit, help;
40 static cmd flush_cmd, search_cmd, regs_cmd, exit_cmd;
41 static cmd print_context_cmd;
42 static cmd backtrace_cmd, purify_cmd, catchers_cmd;
43 static cmd grab_sigs_cmd;
44
45 static struct cmd {
46     char *cmd, *help;
47     void (*fn)(char **ptr);
48 } Cmds[] = {
49     {"help", "Display this help information.", help},
50     {"?", "help", help},
51     {"backtrace", "Backtrace up to N frames.", backtrace_cmd},
52     {"call", "Call FUNCTION with ARG1, ARG2, ...", call_cmd},
53     {"catchers", "Print a list of all the active catchers.", catchers_cmd},
54     {"context", "Print interrupt context number I.", print_context_cmd},
55     {"dump", "Dump memory starting at ADDRESS for COUNT words.", dump_cmd},
56     {"d", "dump", dump_cmd},
57     {"exit", "Exit this instance of the monitor.", exit_cmd},
58     {"flush", "Flush all temp variables.", flush_cmd},
59     /* (Classic CMU CL had a "gc" command here, which seems like a
60      * reasonable idea, but the code was stale (incompatible with
61      * gencgc) so I just flushed it. -- WHN 20000814 */
62     {"grab-signals", "Set the signal handlers to call LDB.", grab_sigs_cmd},
63     {"purify", "Purify. (Caveat purifier!)", purify_cmd},
64     {"print", "Print object at ADDRESS.", print_cmd},
65     {"p", "print", print_cmd},
66     {"quit", "Quit.", quit},
67     {"regs", "Display current Lisp regs.", regs_cmd},
68     {"search", "Search for TYPE starting at ADDRESS for a max of COUNT words.", search_cmd},
69     {"s", "search", search_cmd},
70     {NULL, NULL, NULL}
71 };
72
73 static jmp_buf curbuf;
74
75 static int visible(unsigned char c)
76 {
77     if (c < ' ' || c > '~')
78         return ' ';
79     else
80         return c;
81 }
82
83 static void dump_cmd(char **ptr)
84 {
85     static char *lastaddr = 0;
86     static int lastcount = 20;
87
88     char *addr = lastaddr;
89     int count = lastcount, displacement;
90
91     if (more_p(ptr)) {
92         addr = parse_addr(ptr);
93
94         if (more_p(ptr))
95             count = parse_number(ptr);
96     }
97
98     if (count == 0) {
99         printf("COUNT must be non-zero.\n");
100         return;
101     }
102
103     lastcount = count;
104
105     if (count > 0)
106         displacement = 4;
107     else {
108         displacement = -4;
109         count = -count;
110     }
111
112     while (count-- > 0) {
113 #ifndef alpha
114         printf("0x%08lX: ", (unsigned long) addr);
115 #else
116         printf("0x%08X: ", (u32) addr);
117 #endif
118         if (is_valid_lisp_addr((os_vm_address_t)addr)) {
119 #ifndef alpha
120             unsigned long *lptr = (unsigned long *)addr;
121 #else
122             u32 *lptr = (unsigned long *)addr;
123 #endif
124             unsigned short *sptr = (unsigned short *)addr;
125             unsigned char *cptr = (unsigned char *)addr;
126
127             printf("0x%08lx   0x%04x 0x%04x   0x%02x 0x%02x 0x%02x 0x%02x    %c%c%c%c\n", lptr[0], sptr[0], sptr[1], cptr[0], cptr[1], cptr[2], cptr[3], visible(cptr[0]), visible(cptr[1]), visible(cptr[2]), visible(cptr[3]));
128         }
129         else
130             printf("invalid Lisp-level address\n");
131
132         addr += displacement;
133     }
134
135     lastaddr = addr;
136 }
137
138 static void print_cmd(char **ptr)
139 {
140     lispobj obj = parse_lispobj(ptr);
141
142     print(obj);
143 }
144
145 static void regs_cmd(char **ptr)
146 {
147     printf("CSP\t=\t0x%08lX\n", (unsigned long)current_control_stack_pointer);
148     printf("FP\t=\t0x%08lX\n", (unsigned long)current_control_frame_pointer);
149 #if !defined(ibmrt) && !defined(__i386__)
150     printf("BSP\t=\t0x%08X\n", (unsigned long)current_binding_stack_pointer);
151 #endif
152 #ifdef __i386__
153     printf("BSP\t=\t0x%08X\n", SymbolValue(BINDING_STACK_POINTER));
154 #endif
155
156     printf("DYNAMIC\t=\t0x%08lX\n", (unsigned long)DYNAMIC_SPACE_START);
157 #if defined(ibmrt) || defined(__i386__)
158     printf("ALLOC\t=\t0x%08lX\n", SymbolValue(ALLOCATION_POINTER));
159     printf("TRIGGER\t=\t0x%08lX\n", SymbolValue(INTERNAL_GC_TRIGGER));
160 #else
161     printf("ALLOC\t=\t0x%08X\n",
162            (unsigned long)dynamic_space_free_pointer);
163     printf("TRIGGER\t=\t0x%08X\n", (unsigned long)current_auto_gc_trigger);
164 #endif
165     printf("STATIC\t=\t0x%08lX\n", SymbolValue(STATIC_SPACE_FREE_POINTER));
166     printf("RDONLY\t=\t0x%08lX\n", SymbolValue(READ_ONLY_SPACE_FREE_POINTER));
167
168 #ifdef MIPS
169     printf("FLAGS\t=\t0x%08x\n", current_flags_register);
170 #endif
171 }
172
173 static void search_cmd(char **ptr)
174 {
175     static int lastval = 0, lastcount = 0;
176     static lispobj *start = 0, *end = 0;
177     int val, count;
178     lispobj *addr, obj;
179
180     if (more_p(ptr)) {
181         val = parse_number(ptr);
182         if (val < 0 || val > 0xff) {
183             printf("Can only search for single bytes.\n");
184             return;
185         }
186         if (more_p(ptr)) {
187             addr = (lispobj *)PTR((long)parse_addr(ptr));
188             if (more_p(ptr)) {
189                 count = parse_number(ptr);
190             }
191             else {
192                 /* Speced value and address, but no count. Only one. */
193                 count = -1;
194             }
195         }
196         else {
197             /* Speced a value, but no address, so search same range. */
198             addr = start;
199             count = lastcount;
200         }
201     }
202     else {
203         /* Speced nothing, search again for val. */
204         val = lastval;
205         addr = end;
206         count = lastcount;
207     }
208
209     lastval = val;
210     start = end = addr;
211     lastcount = count;
212
213     printf("searching for 0x%x at 0x%08lX\n", val, (unsigned long)end);
214
215     while (search_for_type(val, &end, &count)) {
216         printf("found 0x%x at 0x%08lX:\n", val, (unsigned long)end);
217         obj = *end;
218         addr = end;
219         end += 2;
220         if (TypeOf(obj) == type_FunctionHeader)
221             print((long)addr | type_FunctionPointer);
222         else if (LowtagOf(obj) == type_OtherImmediate0 || LowtagOf(obj) == type_OtherImmediate1)
223             print((lispobj)addr | type_OtherPointer);
224         else
225             print((lispobj)addr);
226         if (count == -1)
227             return;
228     }
229 }
230
231 static void call_cmd(char **ptr)
232 {
233     lispobj thing = parse_lispobj(ptr), function, result, cons, args[3];
234     int numargs;
235
236     if (LowtagOf(thing) == type_OtherPointer) {
237         switch (TypeOf(*(lispobj *)(thing-type_OtherPointer))) {
238           case type_SymbolHeader:
239             for (cons = SymbolValue(INITIAL_FDEFN_OBJECTS);
240                  cons != NIL;
241                  cons = CONS(cons)->cdr) {
242                 if (FDEFN(CONS(cons)->car)->name == thing) {
243                     thing = CONS(cons)->car;
244                     goto fdefn;
245                 }
246             }
247             printf("symbol 0x%08lx is undefined.\n", thing);
248             return;
249
250           case type_Fdefn:
251           fdefn:
252             function = FDEFN(thing)->function;
253             if (function == NIL) {
254                 printf("fdefn 0x%08lx is undefined.\n", thing);
255                 return;
256             }
257             break;
258           default:
259             printf(
260               "0x%08lx is not a function pointer, symbol, or fdefn object.\n",
261                    thing);
262             return;
263         }
264     }
265     else if (LowtagOf(thing) != type_FunctionPointer) {
266         printf("0x%08lx is not a function pointer, symbol, or fdefn object.\n",
267                thing);
268         return;
269     }
270     else
271         function = thing;
272
273     numargs = 0;
274     while (more_p(ptr)) {
275         if (numargs >= 3) {
276             printf("Too many arguments. (3 at most)\n");
277             return;
278         }
279         args[numargs++] = parse_lispobj(ptr);
280     }
281
282     switch (numargs) {
283       case 0:
284         result = funcall0(function);
285         break;
286       case 1:
287         result = funcall1(function, args[0]);
288         break;
289       case 2:
290         result = funcall2(function, args[0], args[1]);
291         break;
292       case 3:
293         result = funcall3(function, args[0], args[1], args[2]);
294         break;
295     }
296
297     print(result);
298 }
299
300 static void flush_cmd(char **ptr)
301 {
302     flush_vars();
303 }
304
305 static void quit(char **ptr)
306 {
307     char buf[10];
308
309     printf("Really quit? [y] ");
310     fflush(stdout);
311     fgets(buf, sizeof(buf), stdin);
312     if (buf[0] == 'y' || buf[0] == 'Y' || buf[0] == '\n')
313         exit(0);
314 }
315
316 static void help(char **ptr)
317 {
318     struct cmd *cmd;
319
320     for (cmd = Cmds; cmd->cmd != NULL; cmd++)
321         if (cmd->help != NULL)
322             printf("%s\t%s\n", cmd->cmd, cmd->help);
323 }
324
325 static int done;
326
327 static void exit_cmd(char **ptr)
328 {
329     done = 1;
330 }
331
332 static void purify_cmd(char **ptr)
333 {
334     purify(NIL, NIL);
335 }
336
337 static void print_context(os_context_t *context)
338 {
339         int i;
340
341         for (i = 0; i < NREGS; i++) {
342                 printf("%s:\t", lisp_register_names[i]);
343 #ifdef __i386__
344                 brief_print((lispobj)(*os_context_register_addr(context,
345                                                                 i*2)));
346 #else
347                 brief_print((lispobj)(*os_context_register_addr(context,
348                                                                 i)));
349 #endif
350         }
351         printf("PC:\t\t  0x%08lx\n", *os_context_pc_addr(context));
352 }
353
354 static void print_context_cmd(char **ptr)
355 {
356         int free;
357
358         free = SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX)>>2;
359         
360         if (more_p(ptr)) {
361                 int index;
362
363                 index = parse_number(ptr);
364
365                 if ((index >= 0) && (index < free)) {
366                         printf("There are %d interrupt contexts.\n", free);
367                         printf("Printing context %d\n", index);
368                         print_context(lisp_interrupt_contexts[index]);
369                 } else {
370                         printf("There aren't that many/few contexts.\n");
371                         printf("There are %d interrupt contexts.\n", free);
372                 }
373         } else {
374                 if (free == 0)
375                         printf("There are no interrupt contexts!\n");
376                 else {
377                         printf("There are %d interrupt contexts.\n", free);
378                         printf("Printing context %d\n", free - 1);
379                         print_context(lisp_interrupt_contexts[free - 1]);
380                 }
381         }
382 }
383
384 static void backtrace_cmd(char **ptr)
385 {
386     void backtrace(int frames);
387     int n;
388
389     if (more_p(ptr))
390         n = parse_number(ptr);
391     else
392         n = 100;
393
394     printf("Backtrace:\n");
395     backtrace(n);
396 }
397
398 static void catchers_cmd(char **ptr)
399 {
400     struct catch_block *catch;
401
402     catch = (struct catch_block *)SymbolValue(CURRENT_CATCH_BLOCK);
403
404     if (catch == NULL)
405         printf("There are no active catchers!\n");
406     else {
407         while (catch != NULL) {
408 #ifndef __i386__
409             printf("0x%08lX:\n\tuwp: 0x%08lX\n\tfp: 0x%08lX\n\tcode: 0x%08lx\n\tentry: 0x%08lx\n\ttag: ",
410                    (unsigned long)catch, (unsigned long)(catch->current_uwp),
411                    (unsigned long)(catch->current_cont),
412                    catch->current_code,
413                    catch->entry_pc);
414 #else
415             printf("0x%08lX:\n\tuwp: 0x%08lX\n\tfp: 0x%08lX\n\tcode: 0x%08lx\n\tentry: 0x%08lx\n\ttag: ",
416                    (unsigned long)catch, (unsigned long)(catch->current_uwp),
417                    (unsigned long)(catch->current_cont),
418                    component_ptr_from_pc(catch->entry_pc) + type_OtherPointer,
419                    catch->entry_pc);
420 #endif
421             brief_print((lispobj)catch->tag);
422             catch = catch->previous_catch;
423         }
424     }
425 }
426
427 static void grab_sigs_cmd(char **ptr)
428 {
429     extern void sigint_init(void);
430
431     printf("Grabbing signals.\n");
432     sigint_init();
433 }
434
435 static void sub_monitor(void)
436 {
437     struct cmd *cmd, *found;
438     char buf[256];
439     char *line, *ptr, *token;
440     int ambig;
441
442     while (!done) {
443         printf("ldb> ");
444         fflush(stdout);
445         line = fgets(buf, sizeof(buf), stdin);
446         if (line == NULL) {
447             if (isatty(0)) {
448                 putchar('\n');
449                 continue;
450             }
451             else {
452                 fprintf(stderr, "\nEOF on something other than a tty.\n");
453                 exit(0);
454             }
455         }
456         ptr = line;
457         if ((token = parse_token(&ptr)) == NULL)
458             continue;
459         ambig = 0;
460         found = NULL;
461         for (cmd = Cmds; cmd->cmd != NULL; cmd++) {
462             if (strcmp(token, cmd->cmd) == 0) {
463                 found = cmd;
464                 ambig = 0;
465                 break;
466             }
467             else if (strncmp(token, cmd->cmd, strlen(token)) == 0) {
468                 if (found)
469                     ambig = 1;
470                 else
471                     found = cmd;
472             }
473         }
474         if (ambig)
475             printf("``%s'' is ambiguous.\n", token);
476         else if (found == NULL)
477             printf("unknown command: ``%s''\n", token);
478         else {
479             reset_printer();
480             (*found->fn)(&ptr);
481         }
482     }
483 }
484
485 void ldb_monitor()
486 {
487     jmp_buf oldbuf;
488
489     bcopy(curbuf, oldbuf, sizeof(oldbuf));
490
491     printf("LDB monitor\n");
492
493     setjmp(curbuf);
494
495     sub_monitor();
496
497     done = 0;
498
499     bcopy(oldbuf, curbuf, sizeof(curbuf));
500 }
501
502 void throw_to_monitor()
503 {
504     longjmp(curbuf, 1);
505 }