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