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