1 /***********************************************************************/
5 /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
7 /* Copyright 2000 Institut National de Recherche en Informatique et */
8 /* en Automatique. All rights reserved. This file is distributed */
9 /* under the terms of the GNU Library General Public License, with */
10 /* the special exception on linking described in file ../LICENSE. */
12 /***********************************************************************/
14 /* $Id: backtrace.c 9300 2009-06-18 11:17:16Z xclerc $ */
16 /* Stack backtrace for uncaught exceptions */
36 #include "backtrace.h"
38 CAMLexport int caml_backtrace_active = 0;
39 CAMLexport int caml_backtrace_pos = 0;
40 CAMLexport code_t * caml_backtrace_buffer = NULL;
41 CAMLexport value caml_backtrace_last_exn = Val_unit;
42 CAMLexport char * caml_cds_file = NULL;
43 #define BACKTRACE_BUFFER_SIZE 1024
45 /* Location of fields in the Instruct.debug_event record */
51 /* Location of fields in the Location.t record. */
56 /* Location of fields in the Lexing.position record. */
64 /* Start or stop the backtrace machinery */
66 CAMLprim value caml_record_backtrace(value vflag)
68 int flag = Int_val(vflag);
70 if (flag != caml_backtrace_active) {
71 caml_backtrace_active = flag;
72 caml_backtrace_pos = 0;
74 caml_register_global_root(&caml_backtrace_last_exn);
76 caml_remove_global_root(&caml_backtrace_last_exn);
78 /* Note: lazy initialization of caml_backtrace_buffer in
79 caml_stash_backtrace to simplify the interface with the thread
85 /* Return the status of the backtrace machinery */
87 CAMLprim value caml_backtrace_status(value vunit)
89 return Val_bool(caml_backtrace_active);
92 /* Store the return addresses contained in the given stack fragment
93 into the backtrace array */
95 void caml_stash_backtrace(value exn, code_t pc, value * sp)
97 code_t end_code = (code_t) ((char *) caml_start_code + caml_code_size);
98 if (pc != NULL) pc = pc - 1;
99 if (exn != caml_backtrace_last_exn) {
100 caml_backtrace_pos = 0;
101 caml_backtrace_last_exn = exn;
103 if (caml_backtrace_buffer == NULL) {
104 caml_backtrace_buffer = malloc(BACKTRACE_BUFFER_SIZE * sizeof(code_t));
105 if (caml_backtrace_buffer == NULL) return;
107 if (caml_backtrace_pos >= BACKTRACE_BUFFER_SIZE) return;
108 if (pc >= caml_start_code && pc < end_code){
109 caml_backtrace_buffer[caml_backtrace_pos++] = pc;
111 for (/*nothing*/; sp < caml_trapsp; sp++) {
112 code_t p = (code_t) *sp;
113 if (p >= caml_start_code && p < end_code) {
114 if (caml_backtrace_pos >= BACKTRACE_BUFFER_SIZE) break;
115 caml_backtrace_buffer[caml_backtrace_pos++] = p;
120 /* Read the debugging info contained in the current bytecode executable.
121 Return a Caml array of Caml lists of debug_event records in "events",
122 or Val_false on failure. */
128 static value read_debug_info(void)
134 struct exec_trailer trail;
135 struct channel * chan;
136 uint32 num_events, orig, i;
139 if (caml_cds_file != NULL) {
140 exec_name = caml_cds_file;
142 exec_name = caml_exe_name;
144 fd = caml_attempt_open(&exec_name, &trail, 1);
145 if (fd < 0) CAMLreturn(Val_false);
146 caml_read_section_descriptors(fd, &trail);
147 if (caml_seek_optional_section(fd, &trail, "DBUG") == -1) {
149 CAMLreturn(Val_false);
151 chan = caml_open_descriptor_in(fd);
152 num_events = caml_getword(chan);
153 events = caml_alloc(num_events, 0);
154 for (i = 0; i < num_events; i++) {
155 orig = caml_getword(chan);
156 evl = caml_input_val(chan);
157 /* Relocate events in event list */
158 for (l = evl; l != Val_int(0); l = Field(l, 1)) {
159 value ev = Field(l, 0);
160 Field(ev, EV_POS) = Val_long(Long_val(Field(ev, EV_POS)) + orig);
162 /* Record event list */
163 Store_field(events, i, evl);
165 caml_close_channel(chan);
169 /* Search the event for the given PC. Return Val_false if not found. */
171 static value event_for_location(value events, code_t pc)
174 value pos, l, ev, ev_pos, best_ev;
177 Assert(pc >= caml_start_code && pc < caml_start_code + caml_code_size);
178 pos = Val_long((char *) pc - (char *) caml_start_code);
179 for (i = 0; i < Wosize_val(events); i++) {
180 for (l = Field(events, i); l != Val_int(0); l = Field(l, 1)) {
182 ev_pos = Field(ev, EV_POS);
183 if (ev_pos == pos) return ev;
184 /* ocamlc sometimes moves an event past a following PUSH instruction;
185 allow mismatch by 1 instruction. */
186 if (ev_pos == pos + 8) best_ev = ev;
189 if (best_ev != 0) return best_ev;
193 /* Extract location information for the given PC */
204 static void extract_location_info(value events, code_t pc,
205 /*out*/ struct loc_info * li)
209 ev = event_for_location(events, pc);
210 li->loc_is_raise = caml_is_instruction(*pc, RAISE);
211 if (ev == Val_false) {
216 ev_start = Field (Field (ev, EV_LOC), LOC_START);
217 li->loc_filename = String_val (Field (ev_start, POS_FNAME));
218 li->loc_lnum = Int_val (Field (ev_start, POS_LNUM));
220 Int_val (Field (ev_start, POS_CNUM))
221 - Int_val (Field (ev_start, POS_BOL));
223 Int_val (Field (Field (Field (ev, EV_LOC), LOC_END), POS_CNUM))
224 - Int_val (Field (ev_start, POS_BOL));
227 /* Print location information */
229 static void print_location(struct loc_info * li, int index)
233 /* Ignore compiler-inserted raise */
234 if (!li->loc_valid && li->loc_is_raise) return;
236 if (li->loc_is_raise) {
237 /* Initial raise if index == 0, re-raise otherwise */
241 info = "Re-raised at";
244 info = "Raised by primitive operation at";
246 info = "Called from";
248 if (! li->loc_valid) {
249 fprintf(stderr, "%s unknown location\n", info);
251 fprintf (stderr, "%s file \"%s\", line %d, characters %d-%d\n",
252 info, li->loc_filename, li->loc_lnum,
253 li->loc_startchr, li->loc_endchr);
257 /* Print a backtrace */
259 CAMLexport void caml_print_exception_backtrace(void)
265 events = read_debug_info();
266 if (events == Val_false) {
268 "(Program not linked with -g, cannot print stack backtrace)\n");
271 for (i = 0; i < caml_backtrace_pos; i++) {
272 extract_location_info(events, caml_backtrace_buffer[i], &li);
273 print_location(&li, i);
277 /* Convert the backtrace to a data structure usable from Caml */
279 CAMLprim value caml_get_exception_backtrace(value unit)
282 CAMLlocal5(events, res, arr, p, fname);
286 events = read_debug_info();
287 if (events == Val_false) {
288 res = Val_int(0); /* None */
290 arr = caml_alloc(caml_backtrace_pos, 0);
291 for (i = 0; i < caml_backtrace_pos; i++) {
292 extract_location_info(events, caml_backtrace_buffer[i], &li);
294 fname = caml_copy_string(li.loc_filename);
295 p = caml_alloc_small(5, 0);
296 Field(p, 0) = Val_bool(li.loc_is_raise);
298 Field(p, 2) = Val_int(li.loc_lnum);
299 Field(p, 3) = Val_int(li.loc_startchr);
300 Field(p, 4) = Val_int(li.loc_endchr);
302 p = caml_alloc_small(1, 1);
303 Field(p, 0) = Val_bool(li.loc_is_raise);
305 caml_modify(&Field(arr, i), p);
307 res = caml_alloc_small(1, 0); Field(res, 0) = arr; /* Some */