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