Initial revision
[sbcl.git] / src / runtime / backtrace.c
1 /*
2  * simple backtrace facility
3  */
4
5 /*
6  * This software is part of the SBCL system. See the README file for
7  * more information.
8  *
9  * This software is derived from the CMU CL system, which was
10  * written at Carnegie Mellon University and released into the
11  * public domain. The software is in the public domain and is
12  * provided with absolutely no warranty. See the COPYING and CREDITS
13  * files for more information.
14  */
15
16 /*
17  * $Header$
18  */
19
20 #include <stdio.h>
21 #include <signal.h>
22 #include "runtime.h"
23 #include "sbcl.h"
24 #include "globals.h"
25 #include "os.h"
26 #include "interrupt.h"
27 #include "lispregs.h"
28
29 #ifndef __i386__
30
31 /* KLUDGE: Sigh ... I know what the call frame looks like and it had
32  * better not change. */
33
34 struct call_frame {
35 #ifndef alpha
36         struct call_frame *old_cont;
37 #else
38         u32 old_cont;
39 #endif
40         lispobj saved_lra;
41         lispobj code;
42         lispobj other_state[5];
43 };
44
45 struct call_info {
46 #ifndef alpha
47     struct call_frame *frame;
48 #else
49     u32 frame;
50 #endif
51     int interrupted;
52 #ifndef alpha
53     struct code *code;
54 #else
55     u32 code;
56 #endif
57     lispobj lra;
58     int pc; /* Note: this is the trace file offset, not the actual pc. */
59 };
60
61 #define HEADER_LENGTH(header) ((header)>>8)
62
63 static int previous_info(struct call_info *info);
64
65 static struct code *
66 code_pointer(lispobj object)
67 {
68     lispobj *headerp, header;
69     int type, len;
70
71     headerp = (lispobj *) PTR(object);
72     header = *headerp;
73     type = TypeOf(header);
74
75     switch (type) {
76         case type_CodeHeader:
77             break;
78         case type_ReturnPcHeader:
79         case type_FunctionHeader:
80         case type_ClosureFunctionHeader:
81             len = HEADER_LENGTH(header);
82             if (len == 0)
83                 headerp = NULL;
84             else
85                 headerp -= len;
86             break;
87         default:
88             headerp = NULL;
89     }
90
91     return (struct code *) headerp;
92 }
93
94 static boolean
95 cs_valid_pointer_p(struct call_frame *pointer)
96 {
97         return (((char *) control_stack <= (char *) pointer) &&
98                 ((char *) pointer < (char *) current_control_stack_pointer));
99 }
100
101 static void
102 call_info_from_lisp_state(struct call_info *info)
103 {
104     info->frame = (struct call_frame *)current_control_frame_pointer;
105     info->interrupted = 0;
106     info->code = NULL;
107     info->lra = 0;
108     info->pc = 0;
109
110     previous_info(info);
111 }
112
113 static void
114 call_info_from_context(struct call_info *info, os_context_t *context)
115 {
116     unsigned long pc;
117
118     info->interrupted = 1;
119     if (LowtagOf(*os_context_register_addr(context, reg_CODE))
120         == type_FunctionPointer) {
121         /* We tried to call a function, but crapped out before $CODE could
122          * be fixed up. Probably an undefined function. */
123         info->frame =
124             (struct call_frame *)(*os_context_register_addr(context,
125                                                             reg_OCFP));
126         info->lra = (lispobj)(*os_context_register_addr(context, reg_LRA));
127         info->code = code_pointer(info->lra);
128         pc = (unsigned long)PTR(info->lra);
129     }
130     else {
131         info->frame =
132             (struct call_frame *)(*os_context_register_addr(context, reg_CFP));
133         info->code =
134             code_pointer(*os_context_register_addr(context, reg_CODE));
135         info->lra = NIL;
136         pc = *os_context_pc_addr(context);
137     }
138     if (info->code != NULL)
139         info->pc = pc - (unsigned long) info->code -
140 #ifndef alpha
141             (HEADER_LENGTH(info->code->header) * sizeof(lispobj));
142 #else
143             (HEADER_LENGTH(((struct code *)info->code)->header) * sizeof(lispobj));
144 #endif
145     else
146         info->pc = 0;
147 }
148
149 static int
150 previous_info(struct call_info *info)
151 {
152     struct call_frame *this_frame;
153     int free;
154
155     if (!cs_valid_pointer_p(info->frame)) {
156         printf("Bogus callee value (0x%08x).\n", (unsigned long)info->frame);
157         return 0;
158     }
159
160     this_frame = info->frame;
161     info->lra = this_frame->saved_lra;
162     info->frame = this_frame->old_cont;
163     info->interrupted = 0;
164
165     if (info->frame == NULL || info->frame == this_frame)
166         return 0;
167
168     if (info->lra == NIL) {
169         /* We were interrupted. Find the correct signal context. */
170         free = SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX)>>2;
171         while (free-- > 0) {
172             os_context_t *context = 
173                 lisp_interrupt_contexts[free];
174             if ((struct call_frame *)(*os_context_register_addr(context,
175                                                                 reg_CFP))
176                 == info->frame) {
177                 call_info_from_context(info, context);
178                 break;
179             }
180         }
181     }
182     else {
183         info->code = code_pointer(info->lra);
184         if (info->code != NULL)
185             info->pc = (unsigned long)PTR(info->lra) -
186                 (unsigned long)info->code -
187 #ifndef alpha
188                 (HEADER_LENGTH(info->code->header) * sizeof(lispobj));
189 #else
190                 (HEADER_LENGTH(((struct code *)info->code)->header) * sizeof(lispobj));
191 #endif
192         else
193             info->pc = 0;
194     }
195
196     return 1;
197 }
198
199 void
200 backtrace(int nframes)
201 {
202     struct call_info info;
203         
204     call_info_from_lisp_state(&info);
205
206     do {
207         printf("<Frame 0x%08x%s, ", (unsigned long) info.frame,
208                 info.interrupted ? " [interrupted]" : "");
209
210         if (info.code != (struct code *) 0) {
211             lispobj function;
212
213             printf("CODE: 0x%08X, ", (unsigned long) info.code | type_OtherPointer);
214
215 #ifndef alpha
216             function = info.code->entry_points;
217 #else
218             function = ((struct code *)info.code)->entry_points;
219 #endif
220             while (function != NIL) {
221                 struct function *header;
222                 lispobj name;
223
224                 header = (struct function *) PTR(function);
225                 name = header->name;
226
227                 if (LowtagOf(name) == type_OtherPointer) {
228                     lispobj *object;
229
230                     object = (lispobj *) PTR(name);
231
232                     if (TypeOf(*object) == type_SymbolHeader) {
233                         struct symbol *symbol;
234
235                         symbol = (struct symbol *) object;
236                         object = (lispobj *) PTR(symbol->name);
237                     }
238                     if (TypeOf(*object) == type_SimpleString) {
239                         struct vector *string;
240
241                         string = (struct vector *) object;
242                         printf("%s, ", (char *) string->data);
243                     } else
244                         printf("(Not simple string??\?), ");
245                 } else
246                     printf("(Not other pointer??\?), ");
247
248
249                 function = header->next;
250             }
251         }
252         else
253             printf("CODE: ???, ");
254
255         if (info.lra != NIL)
256             printf("LRA: 0x%08x, ", (unsigned long)info.lra);
257         else
258             printf("<no LRA>, ");
259
260         if (info.pc)
261             printf("PC: 0x%x>\n", info.pc);
262         else
263             printf("PC: ??\?>\n");
264
265     } while (--nframes > 0 && previous_info(&info));
266 }
267
268 #else
269
270 void
271 backtrace(int nframes)
272 {
273     printf("Can't backtrace on this hardware platform.\n");
274 }
275
276 #endif