* boot-9.scm (error-catching-loop, save-stack): `the-last-stack'
[bpt/guile.git] / libguile / backtrace.c
CommitLineData
ab4f3efb 1/* Printing of backtraces and error messages
1e598865 2 * Copyright (C) 1996,1997 Free Software Foundation
ab4f3efb
MD
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; see the file COPYING. If not, write to
82892bed
JB
16 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307 USA
ab4f3efb
MD
18 *
19 * As a special exception, the Free Software Foundation gives permission
20 * for additional uses of the text contained in its release of GUILE.
21 *
22 * The exception is that, if you link the GUILE library with other files
23 * to produce an executable, this does not by itself cause the
24 * resulting executable to be covered by the GNU General Public License.
25 * Your use of that executable is in no way restricted on account of
26 * linking the GUILE library code into it.
27 *
28 * This exception does not however invalidate any other reasons why
29 * the executable file might be covered by the GNU General Public License.
30 *
31 * This exception applies only to the code released by the
32 * Free Software Foundation under the name GUILE. If you copy
33 * code from other Free Software Foundation releases into a copy of
34 * GUILE, as the General Public License permits, the exception does
35 * not apply to the code that you add in this way. To avoid misleading
36 * anyone as to the status of such modified files, you must delete
37 * this exception notice from them.
38 *
39 * If you write modifications of your own for GUILE, it is your choice
40 * whether to permit this exception to apply to your modifications.
41 * If you do not wish that, delete this exception notice.
42 *
43 * The author can be reached at djurfeldt@nada.kth.se
82892bed 44 * Mikael Djurfeldt, SANS/NADA KTH, 10044 STOCKHOLM, SWEDEN */
ab4f3efb
MD
45
46#include <stdio.h>
47#include "_scm.h"
48#include "stacks.h"
49#include "srcprop.h"
50#include "genio.h"
51#include "struct.h"
52#include "strports.h"
bdf8afff 53#include "throw.h"
a5d6d578 54#include "fluids.h"
ab4f3efb
MD
55
56#include "backtrace.h"
57
58/* {Error reporting and backtraces}
59 * (A first approximation.)
60 *
61 * Note that these functions shouldn't generate errors themselves.
62 */
63
64#ifndef SCM_RECKLESS
65#undef SCM_ASSERT
66#define SCM_ASSERT(_cond, _arg, _pos, _subr) \
67 if (!(_cond)) \
68 return SCM_BOOL_F;
69#endif
70
71static void display_header SCM_P ((SCM source, SCM port));
72static void
73display_header (source, port)
74 SCM source;
75 SCM port;
76{
77 SCM fname = (SCM_NIMP (source) && SCM_MEMOIZEDP (source)
78 ? scm_source_property (source, scm_i_filename)
79 : SCM_BOOL_F);
80 if (SCM_NIMP (fname) && SCM_STRINGP (fname))
81 {
82 scm_prin1 (fname, port, 0);
b7f3516f 83 scm_putc (':', port);
ab4f3efb 84 scm_prin1 (scm_source_property (source, scm_i_line), port, 0);
b7f3516f 85 scm_putc (':', port);
ab4f3efb
MD
86 scm_prin1 (scm_source_property (source, scm_i_column), port, 0);
87 }
88 else
b7f3516f
TT
89 scm_puts ("ERROR", port);
90 scm_puts (": ", port);
ab4f3efb
MD
91}
92
f3acc5c1
JB
93
94void
95scm_display_error_message (message, args, port)
ab4f3efb
MD
96 SCM message;
97 SCM args;
98 SCM port;
99{
100 int writingp;
101 char *start;
102 char *p;
103
89958ad0 104 if (SCM_IMP (message) || !SCM_ROSTRINGP (message) || SCM_IMP (args)
c37e0e55 105 || !scm_list_p (args))
ab4f3efb
MD
106 {
107 scm_prin1 (message, port, 0);
b7f3516f 108 scm_putc ('\n', port);
ab4f3efb
MD
109 return;
110 }
111
89958ad0
JB
112 SCM_COERCE_SUBSTR (message);
113 start = SCM_ROCHARS (message);
ab4f3efb
MD
114 for (p = start; *p != '\0'; ++p)
115 if (*p == '%')
116 {
117 if (SCM_IMP (args) || SCM_NCONSP (args))
118 continue;
119
120 ++p;
121 if (*p == 's')
122 writingp = 0;
123 else if (*p == 'S')
124 writingp = 1;
125 else
126 continue;
127
b7f3516f 128 scm_lfwrite (start, p - start - 1, port);
ab4f3efb
MD
129 scm_prin1 (SCM_CAR (args), port, writingp);
130 args = SCM_CDR (args);
131 start = p + 1;
132 }
b7f3516f
TT
133 scm_lfwrite (start, p - start, port);
134 scm_putc ('\n', port);
ab4f3efb
MD
135}
136
137static void display_expression SCM_P ((SCM frame, SCM pname, SCM source, SCM port));
138static void
139display_expression (frame, pname, source, port)
140 SCM frame;
141 SCM pname;
142 SCM source;
143 SCM port;
144{
145 SCM print_state = scm_make_print_state ();
146 scm_print_state *pstate = SCM_PRINT_STATE (print_state);
147 pstate->writingp = 0;
148 pstate->fancyp = 1;
149 pstate->level = 2;
150 pstate->length = 3;
151 if (SCM_NIMP (pname) && SCM_ROSTRINGP (pname))
152 {
153 if (SCM_NIMP (frame)
154 && SCM_FRAMEP (frame)
155 && SCM_FRAME_EVAL_ARGS_P (frame))
b7f3516f 156 scm_puts ("While evaluating arguments to ", port);
ab4f3efb 157 else
b7f3516f 158 scm_puts ("In procedure ", port);
ab4f3efb
MD
159 scm_iprin1 (pname, port, pstate);
160 if (SCM_NIMP (source) && SCM_MEMOIZEDP (source))
161 {
b7f3516f 162 scm_puts (" in expression ", port);
ab4f3efb
MD
163 pstate->writingp = 1;
164 scm_iprin1 (scm_unmemoize (source), port, pstate);
165 }
166 }
167 else if (SCM_NIMP (source))
168 {
b7f3516f 169 scm_puts ("In expression ", port);
ab4f3efb
MD
170 pstate->writingp = 1;
171 scm_iprin1 (scm_unmemoize (source), port, pstate);
172 }
b7f3516f 173 scm_puts (":\n", port);
ab4f3efb
MD
174 scm_free_print_state (print_state);
175}
176
bdf8afff
MD
177struct display_error_args {
178 SCM stack;
179 SCM port;
180 SCM subr;
181 SCM message;
182 SCM args;
183 SCM rest;
184};
185
186static SCM
187display_error_body (struct display_error_args *a, SCM jmpbuf)
ab4f3efb
MD
188{
189 SCM current_frame = SCM_BOOL_F;
190 SCM source = SCM_BOOL_F;
191 SCM pname = SCM_BOOL_F;
841076ac 192 if (SCM_DEBUGGINGP
bdf8afff
MD
193 && SCM_NIMP (a->stack)
194 && SCM_STACKP (a->stack)
195 && SCM_STACK_LENGTH (a->stack) > 0)
ab4f3efb 196 {
bdf8afff 197 current_frame = scm_stack_ref (a->stack, SCM_INUM0);
ab4f3efb
MD
198 source = SCM_FRAME_SOURCE (current_frame);
199 if (!(SCM_NIMP (source) && SCM_MEMOIZEDP (source)))
200 source = SCM_FRAME_SOURCE (SCM_FRAME_PREV (current_frame));
201 if (SCM_FRAME_PROC_P (current_frame)
016e2ce1 202 && scm_procedure_p (SCM_FRAME_PROC (current_frame)) == SCM_BOOL_T)
ab4f3efb
MD
203 pname = scm_procedure_name (SCM_FRAME_PROC (current_frame));
204 }
205 if (!(SCM_NIMP (pname) && SCM_ROSTRINGP (pname)))
bdf8afff 206 pname = a->subr;
ab4f3efb
MD
207 if ((SCM_NIMP (source) && SCM_MEMOIZEDP (source))
208 || (SCM_NIMP (pname) && SCM_ROSTRINGP (pname)))
209 {
bdf8afff
MD
210 display_header (source, a->port);
211 display_expression (current_frame, pname, source, a->port);
ab4f3efb 212 }
bdf8afff
MD
213 display_header (source, a->port);
214 scm_display_error_message (a->message, a->args, a->port);
215 return SCM_UNSPECIFIED;
216}
217
218struct display_error_handler_data {
219 char *mode;
220 SCM port;
221};
222
223/* This is the exception handler for error reporting routines.
224 Note that it is very important that this handler *doesn't* try to
225 print more than the error tag, since the error very probably is
226 caused by an erroneous print call-back routine. If we would
227 tru to print all objects, we would enter an infinite loop. */
228static SCM
229display_error_handler (struct display_error_handler_data *data,
230 SCM tag, SCM args)
231{
232 SCM print_state = scm_make_print_state ();
b7f3516f
TT
233 scm_puts ("\nException during displaying of ", data->port);
234 scm_puts (data->mode, data->port);
235 scm_puts (": ", data->port);
bdf8afff 236 scm_iprin1 (tag, data->port, SCM_PRINT_STATE (print_state));
b7f3516f 237 scm_putc ('\n', data->port);
bdf8afff
MD
238 return SCM_UNSPECIFIED;
239}
240
241SCM_PROC(s_display_error, "display-error", 6, 0, 0, scm_display_error);
242SCM
243scm_display_error (stack, port, subr, message, args, rest)
244 SCM stack;
245 SCM port;
246 SCM subr;
247 SCM message;
248 SCM args;
249 SCM rest;
250{
251 struct display_error_args a = { stack, port, subr, message, args, rest };
252 struct display_error_handler_data data = { "error", port };
253 scm_internal_catch (SCM_BOOL_T,
254 (scm_catch_body_t) display_error_body, &a,
255 (scm_catch_handler_t) display_error_handler, &data);
ab4f3efb
MD
256 return SCM_UNSPECIFIED;
257}
258
259static void indent SCM_P ((int n, SCM port));
260static void
261indent (n, port)
262 int n;
263 SCM port;
264{
265 int i;
266 for (i = 0; i < n; ++i)
b7f3516f 267 scm_putc (' ', port);
ab4f3efb
MD
268}
269
270static void display_frame_expr SCM_P ((char *hdr, SCM exp, char *tlr, int indentation, SCM sport, SCM port, scm_print_state *pstate));
271static void
272display_frame_expr (hdr, exp, tlr, indentation, sport, port, pstate)
273 char *hdr;
274 SCM exp;
275 char *tlr;
276 int indentation;
277 SCM sport;
278 SCM port;
279 scm_print_state *pstate;
280{
ab4f3efb
MD
281 if (SCM_NIMP (exp) && SCM_CONSP (exp))
282 {
283 scm_iprlist (hdr, exp, tlr[0], port, pstate);
b7f3516f 284 scm_puts (&tlr[1], port);
ab4f3efb
MD
285 }
286 else
287 scm_iprin1 (exp, port, pstate);
b7f3516f 288 scm_putc ('\n', port);
ab4f3efb
MD
289}
290
e3c37929
MD
291static void display_application SCM_P ((SCM frame, int indentation, SCM sport, SCM port, scm_print_state *pstate));
292static void
293display_application (frame, indentation, sport, port, pstate)
294 SCM frame;
295 int indentation;
296 SCM sport;
297 SCM port;
298 scm_print_state *pstate;
299{
300 SCM proc = SCM_FRAME_PROC (frame);
301 SCM name = (SCM_NFALSEP (scm_procedure_p (proc))
302 ? scm_procedure_name (proc)
303 : SCM_BOOL_F);
304 display_frame_expr ("[",
305 scm_cons (SCM_NFALSEP (name) ? name : proc,
306 SCM_FRAME_ARGS (frame)),
307 SCM_FRAME_EVAL_ARGS_P (frame) ? " ..." : "]",
308 indentation,
309 sport,
310 port,
311 pstate);
312}
313
314SCM_PROC(s_display_application, "display-application", 1, 1, 0, scm_display_application);
315
316SCM
317scm_display_application (SCM frame, SCM port)
318{
319 if (SCM_UNBNDP (port))
320 port = scm_cur_outp;
321 if (SCM_FRAME_PROC_P (frame))
322 /* Display an application. */
323 {
324 SCM print_state;
325 scm_print_state *pstate;
326
327 /* Create a print state for printing of frames. */
328 print_state = scm_make_print_state ();
329 pstate = SCM_PRINT_STATE (print_state);
330 pstate->writingp = 1;
331 pstate->fancyp = 1;
332 pstate->level = 2;
333 pstate->length = 9;
334
335 display_application (frame, 0, SCM_BOOL_F, port, pstate); /*fixme*/
336 return SCM_BOOL_T;
337 }
338 else
339 return SCM_BOOL_F;
340}
341
ab4f3efb
MD
342static void display_frame SCM_P ((SCM frame, int nfield, int indentation, SCM sport, SCM port, scm_print_state *pstate));
343static void
344display_frame (frame, nfield, indentation, sport, port, pstate)
345 SCM frame;
346 int nfield;
347 int indentation;
348 SCM sport;
349 SCM port;
350 scm_print_state *pstate;
351{
352 int n, i, j;
353
354 /* Announce missing frames? */
355 if (!SCM_BACKWARDS_P && SCM_FRAME_OVERFLOW_P (frame))
356 {
357 indent (nfield + 1 + indentation, port);
b7f3516f 358 scm_puts ("...\n", port);
ab4f3efb
MD
359 }
360
361 /* Check size of frame number. */
362 n = SCM_FRAME_NUMBER (frame);
363 for (i = 0, j = n; j > 0; ++i) j /= 10;
364
365 /* Number indentation. */
366 indent (nfield - (i ? i : 1), port);
367
368 /* Frame number. */
369 scm_iprin1 (SCM_MAKINUM (n), port, pstate);
370
371 /* Real frame marker */
b7f3516f 372 scm_putc (SCM_FRAME_REAL_P (frame) ? '*' : ' ', port);
ab4f3efb
MD
373
374 /* Indentation. */
375 indent (indentation, port);
376
377 if (SCM_FRAME_PROC_P (frame))
378 /* Display an application. */
e3c37929 379 display_application (frame, nfield + 1 + indentation, sport, port, pstate);
ab4f3efb
MD
380 else
381 /* Display a special form. */
382 {
383 SCM source = SCM_FRAME_SOURCE (frame);
384 SCM copy = scm_source_property (source, scm_i_copy);
385 display_frame_expr ("(",
386 SCM_NIMP (copy) && SCM_CONSP (copy)
387 ? copy
388 : scm_unmemoize (source),
389 ")",
390 nfield + 1 + indentation,
391 sport,
392 port,
393 pstate);
394 }
395
396 /* Announce missing frames? */
397 if (SCM_BACKWARDS_P && SCM_FRAME_OVERFLOW_P (frame))
398 {
399 indent (nfield + 1 + indentation, port);
b7f3516f 400 scm_puts ("...\n", port);
ab4f3efb
MD
401 }
402}
403
bdf8afff
MD
404struct display_backtrace_args {
405 SCM stack;
406 SCM port;
407 SCM first;
408 SCM depth;
409};
410
ab4f3efb 411SCM_PROC(s_display_backtrace, "display-backtrace", 2, 2, 0, scm_display_backtrace);
bdf8afff
MD
412
413static SCM
414display_backtrace_body (struct display_backtrace_args *a, SCM jmpbuf)
ab4f3efb
MD
415{
416 int n_frames, beg, end, n, i, j;
417 int nfield, indent_p, indentation;
418 SCM frame, sport, print_state;
419 scm_print_state *pstate;
420
78446828
MV
421 a->port = SCM_COERCE_OUTPORT (a->port);
422
ab4f3efb 423 /* Argument checking and extraction. */
bdf8afff
MD
424 SCM_ASSERT (SCM_NIMP (a->stack) && SCM_STACKP (a->stack),
425 a->stack,
ab4f3efb
MD
426 SCM_ARG1,
427 s_display_backtrace);
bdf8afff
MD
428 SCM_ASSERT (SCM_NIMP (a->port) && SCM_OPOUTPORTP (a->port),
429 a->port,
ab4f3efb
MD
430 SCM_ARG2,
431 s_display_backtrace);
bdf8afff
MD
432 n_frames = SCM_INUM (scm_stack_length (a->stack));
433 n = SCM_INUMP (a->depth) ? SCM_INUM (a->depth) : SCM_BACKTRACE_DEPTH;
ab4f3efb
MD
434 if (SCM_BACKWARDS_P)
435 {
bdf8afff 436 beg = SCM_INUMP (a->first) ? SCM_INUM (a->first) : 0;
ab4f3efb
MD
437 end = beg + n - 1;
438 if (end >= n_frames)
439 end = n_frames - 1;
440 n = end - beg + 1;
441 }
442 else
443 {
bdf8afff 444 if (SCM_INUMP (a->first))
ab4f3efb 445 {
bdf8afff 446 beg = SCM_INUM (a->first);
ab4f3efb
MD
447 end = beg - n + 1;
448 if (end < 0)
449 end = 0;
450 }
451 else
452 {
453 beg = n - 1;
454 end = 0;
455 if (beg >= n_frames)
456 beg = n_frames - 1;
457 }
458 n = beg - end + 1;
459 }
bdf8afff
MD
460 SCM_ASSERT (beg >= 0 && beg < n_frames, a->first, SCM_ARG3, s_display_backtrace);
461 SCM_ASSERT (n > 0, a->depth, SCM_ARG4, s_display_backtrace);
ab4f3efb
MD
462
463 /* Create a string port used for adaptation of printing parameters. */
464 sport = scm_mkstrport (SCM_INUM0,
465 scm_make_string (SCM_MAKINUM (240), SCM_UNDEFINED),
466 SCM_OPN | SCM_WRTNG,
467 s_display_backtrace);
468
469 /* Create a print state for printing of frames. */
470 print_state = scm_make_print_state ();
471 pstate = SCM_PRINT_STATE (print_state);
472 pstate->writingp = 1;
473 pstate->fancyp = 1;
e3c37929
MD
474 pstate->level = 2;
475 pstate->length = 3;
ab4f3efb
MD
476
477 /* First find out if it's reasonable to do indentation. */
478 if (SCM_BACKWARDS_P)
479 indent_p = 0;
480 else
481 {
482 indent_p = 1;
bdf8afff 483 frame = scm_stack_ref (a->stack, SCM_MAKINUM (beg));
ab4f3efb
MD
484 for (i = 0, j = 0; i < n; ++i)
485 {
486 if (SCM_FRAME_REAL_P (frame))
487 ++j;
488 if (j > SCM_BACKTRACE_INDENT)
489 {
490 indent_p = 0;
491 break;
492 }
493 frame = (SCM_BACKWARDS_P
494 ? SCM_FRAME_PREV (frame)
495 : SCM_FRAME_NEXT (frame));
496 }
497 }
498
499 /* Determine size of frame number field. */
bdf8afff 500 j = SCM_FRAME_NUMBER (scm_stack_ref (a->stack, SCM_MAKINUM (end)));
ab4f3efb
MD
501 for (i = 0; j > 0; ++i) j /= 10;
502 nfield = i ? i : 1;
503
b7f3516f 504 scm_puts ("Backtrace:\n", a->port);
ab4f3efb
MD
505
506 /* Print frames. */
bdf8afff 507 frame = scm_stack_ref (a->stack, SCM_MAKINUM (beg));
ab4f3efb 508 indentation = 1;
bdf8afff 509 display_frame (frame, nfield, indentation, sport, a->port, pstate);
ab4f3efb
MD
510 for (i = 1; i < n; ++i)
511 {
512 if (indent_p && SCM_FRAME_EVAL_ARGS_P (frame))
513 ++indentation;
514 frame = SCM_BACKWARDS_P ? SCM_FRAME_PREV (frame) : SCM_FRAME_NEXT (frame);
bdf8afff 515 display_frame (frame, nfield, indentation, sport, a->port, pstate);
ab4f3efb 516 }
bdf8afff
MD
517
518 return SCM_UNSPECIFIED;
519}
520
521SCM
522scm_display_backtrace (stack, port, first, depth)
523 SCM stack;
524 SCM port;
525 SCM first;
526 SCM depth;
527{
528 struct display_backtrace_args a = { stack, port, first, depth };
529 struct display_error_handler_data data = { "backtrace", port };
530 scm_internal_catch (SCM_BOOL_T,
531 (scm_catch_body_t) display_backtrace_body, &a,
532 (scm_catch_handler_t) display_error_handler, &data);
ab4f3efb
MD
533 return SCM_UNSPECIFIED;
534}
535
78f9f47b 536SCM_VCELL (scm_has_shown_backtrace_hint_p_var, "has-shown-backtrace-hint?");
5aab5d96
MD
537
538SCM_PROC(s_backtrace, "backtrace", 0, 0, 0, scm_backtrace);
539SCM
540scm_backtrace ()
541{
a5d6d578
MD
542 SCM the_last_stack = scm_fluid_ref (SCM_CDR (scm_the_last_stack_var));
543 if (SCM_NFALSEP (the_last_stack))
5aab5d96
MD
544 {
545 scm_newline (scm_cur_outp);
a5d6d578 546 scm_display_backtrace (the_last_stack,
5aab5d96
MD
547 scm_cur_outp,
548 SCM_UNDEFINED,
549 SCM_UNDEFINED);
550 scm_newline (scm_cur_outp);
551 if (SCM_FALSEP (SCM_CDR (scm_has_shown_backtrace_hint_p_var))
552 && !SCM_BACKTRACE_P)
553 {
b7f3516f
TT
554 scm_puts ("Type \"(debug-enable 'backtrace)\" if you would like "
555 "a backtrace\n"
556 "automatically if an error occurs in the future.\n",
557 scm_cur_outp);
5aab5d96
MD
558 SCM_SETCDR (scm_has_shown_backtrace_hint_p_var, SCM_BOOL_T);
559 }
560 }
561 else
562 {
b7f3516f 563 scm_puts ("No backtrace available.\n", scm_cur_outp);
5aab5d96
MD
564 }
565 return SCM_UNSPECIFIED;
566}
567
ab4f3efb
MD
568\f
569
570void
571scm_init_backtrace ()
572{
a5d6d578
MD
573 SCM f = scm_make_fluid ();
574 scm_the_last_stack_var = scm_sysintern ("the-last-stack", f);
5aab5d96 575
ab4f3efb
MD
576#include "backtrace.x"
577}