*** empty log message ***
[bpt/guile.git] / libguile / backtrace.c
CommitLineData
ab4f3efb 1/* Printing of backtraces and error messages
d90ca38d 2 * Copyright (C) 1996,1997,1998 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
b6609fc7
MD
71SCM scm_the_last_stack_fluid;
72
ab4f3efb
MD
73static void display_header SCM_P ((SCM source, SCM port));
74static void
75display_header (source, port)
76 SCM source;
77 SCM port;
78{
79 SCM fname = (SCM_NIMP (source) && SCM_MEMOIZEDP (source)
7f2d92b1 80 ? scm_source_property (source, scm_sym_filename)
ab4f3efb
MD
81 : SCM_BOOL_F);
82 if (SCM_NIMP (fname) && SCM_STRINGP (fname))
83 {
84 scm_prin1 (fname, port, 0);
b7f3516f 85 scm_putc (':', port);
7f2d92b1 86 scm_intprint (SCM_INUM (scm_source_property (source, scm_sym_line)) + 1,
0e929db3
MD
87 10,
88 port);
b7f3516f 89 scm_putc (':', port);
7f2d92b1 90 scm_intprint (SCM_INUM (scm_source_property (source, scm_sym_column)) + 1,
0e929db3
MD
91 10,
92 port);
ab4f3efb
MD
93 }
94 else
b7f3516f
TT
95 scm_puts ("ERROR", port);
96 scm_puts (": ", port);
ab4f3efb
MD
97}
98
f3acc5c1
JB
99
100void
101scm_display_error_message (message, args, port)
ab4f3efb
MD
102 SCM message;
103 SCM args;
104 SCM port;
105{
106 int writingp;
107 char *start;
108 char *p;
109
89958ad0 110 if (SCM_IMP (message) || !SCM_ROSTRINGP (message) || SCM_IMP (args)
c37e0e55 111 || !scm_list_p (args))
ab4f3efb
MD
112 {
113 scm_prin1 (message, port, 0);
b7f3516f 114 scm_putc ('\n', port);
ab4f3efb
MD
115 return;
116 }
117
89958ad0
JB
118 SCM_COERCE_SUBSTR (message);
119 start = SCM_ROCHARS (message);
ab4f3efb
MD
120 for (p = start; *p != '\0'; ++p)
121 if (*p == '%')
122 {
123 if (SCM_IMP (args) || SCM_NCONSP (args))
124 continue;
125
126 ++p;
127 if (*p == 's')
128 writingp = 0;
129 else if (*p == 'S')
130 writingp = 1;
131 else
132 continue;
133
b7f3516f 134 scm_lfwrite (start, p - start - 1, port);
ab4f3efb
MD
135 scm_prin1 (SCM_CAR (args), port, writingp);
136 args = SCM_CDR (args);
137 start = p + 1;
138 }
b7f3516f
TT
139 scm_lfwrite (start, p - start, port);
140 scm_putc ('\n', port);
ab4f3efb
MD
141}
142
143static void display_expression SCM_P ((SCM frame, SCM pname, SCM source, SCM port));
144static void
145display_expression (frame, pname, source, port)
146 SCM frame;
147 SCM pname;
148 SCM source;
149 SCM port;
150{
151 SCM print_state = scm_make_print_state ();
152 scm_print_state *pstate = SCM_PRINT_STATE (print_state);
153 pstate->writingp = 0;
154 pstate->fancyp = 1;
155 pstate->level = 2;
156 pstate->length = 3;
157 if (SCM_NIMP (pname) && SCM_ROSTRINGP (pname))
158 {
159 if (SCM_NIMP (frame)
160 && SCM_FRAMEP (frame)
161 && SCM_FRAME_EVAL_ARGS_P (frame))
b7f3516f 162 scm_puts ("While evaluating arguments to ", port);
ab4f3efb 163 else
b7f3516f 164 scm_puts ("In procedure ", port);
ab4f3efb
MD
165 scm_iprin1 (pname, port, pstate);
166 if (SCM_NIMP (source) && SCM_MEMOIZEDP (source))
167 {
b7f3516f 168 scm_puts (" in expression ", port);
ab4f3efb
MD
169 pstate->writingp = 1;
170 scm_iprin1 (scm_unmemoize (source), port, pstate);
171 }
172 }
173 else if (SCM_NIMP (source))
174 {
b7f3516f 175 scm_puts ("In expression ", port);
ab4f3efb
MD
176 pstate->writingp = 1;
177 scm_iprin1 (scm_unmemoize (source), port, pstate);
178 }
b7f3516f 179 scm_puts (":\n", port);
ab4f3efb
MD
180 scm_free_print_state (print_state);
181}
182
bdf8afff
MD
183struct display_error_args {
184 SCM stack;
185 SCM port;
186 SCM subr;
187 SCM message;
188 SCM args;
189 SCM rest;
190};
191
192static SCM
39752bec 193display_error_body (struct display_error_args *a)
ab4f3efb
MD
194{
195 SCM current_frame = SCM_BOOL_F;
196 SCM source = SCM_BOOL_F;
197 SCM pname = SCM_BOOL_F;
a88a4c8a
JB
198 SCM prev_frame = SCM_BOOL_F;
199
841076ac 200 if (SCM_DEBUGGINGP
bdf8afff
MD
201 && SCM_NIMP (a->stack)
202 && SCM_STACKP (a->stack)
203 && SCM_STACK_LENGTH (a->stack) > 0)
ab4f3efb 204 {
bdf8afff 205 current_frame = scm_stack_ref (a->stack, SCM_INUM0);
ab4f3efb 206 source = SCM_FRAME_SOURCE (current_frame);
a88a4c8a
JB
207 prev_frame = SCM_FRAME_PREV (current_frame);
208 if (!(SCM_NIMP (source) && SCM_MEMOIZEDP (source))
209 && prev_frame != SCM_BOOL_F)
210 source = SCM_FRAME_SOURCE (prev_frame);
ab4f3efb 211 if (SCM_FRAME_PROC_P (current_frame)
016e2ce1 212 && scm_procedure_p (SCM_FRAME_PROC (current_frame)) == SCM_BOOL_T)
ab4f3efb
MD
213 pname = scm_procedure_name (SCM_FRAME_PROC (current_frame));
214 }
215 if (!(SCM_NIMP (pname) && SCM_ROSTRINGP (pname)))
bdf8afff 216 pname = a->subr;
e40a5fc8 217 if ((SCM_NIMP (pname) && SCM_ROSTRINGP (pname))
26e19854 218 || (SCM_NIMP (source) && SCM_MEMOIZEDP (source)))
ab4f3efb 219 {
bdf8afff
MD
220 display_header (source, a->port);
221 display_expression (current_frame, pname, source, a->port);
ab4f3efb 222 }
bdf8afff
MD
223 display_header (source, a->port);
224 scm_display_error_message (a->message, a->args, a->port);
225 return SCM_UNSPECIFIED;
226}
227
228struct display_error_handler_data {
229 char *mode;
230 SCM port;
231};
232
233/* This is the exception handler for error reporting routines.
234 Note that it is very important that this handler *doesn't* try to
235 print more than the error tag, since the error very probably is
236 caused by an erroneous print call-back routine. If we would
237 tru to print all objects, we would enter an infinite loop. */
238static SCM
239display_error_handler (struct display_error_handler_data *data,
240 SCM tag, SCM args)
241{
242 SCM print_state = scm_make_print_state ();
b7f3516f
TT
243 scm_puts ("\nException during displaying of ", data->port);
244 scm_puts (data->mode, data->port);
245 scm_puts (": ", data->port);
bdf8afff 246 scm_iprin1 (tag, data->port, SCM_PRINT_STATE (print_state));
b7f3516f 247 scm_putc ('\n', data->port);
bdf8afff
MD
248 return SCM_UNSPECIFIED;
249}
250
251SCM_PROC(s_display_error, "display-error", 6, 0, 0, scm_display_error);
252SCM
253scm_display_error (stack, port, subr, message, args, rest)
254 SCM stack;
255 SCM port;
256 SCM subr;
257 SCM message;
258 SCM args;
259 SCM rest;
260{
0b2cb4ee
MD
261 struct display_error_args a;
262 struct display_error_handler_data data;
263 a.stack = stack;
264 a.port = port;
265 a.subr = subr;
266 a.message = message;
267 a.args = args;
268 a.rest = rest;
269 data.mode = "error";
270 data.port = port;
bdf8afff
MD
271 scm_internal_catch (SCM_BOOL_T,
272 (scm_catch_body_t) display_error_body, &a,
273 (scm_catch_handler_t) display_error_handler, &data);
ab4f3efb
MD
274 return SCM_UNSPECIFIED;
275}
276
277static void indent SCM_P ((int n, SCM port));
278static void
279indent (n, port)
280 int n;
281 SCM port;
282{
283 int i;
284 for (i = 0; i < n; ++i)
b7f3516f 285 scm_putc (' ', port);
ab4f3efb
MD
286}
287
288static void display_frame_expr SCM_P ((char *hdr, SCM exp, char *tlr, int indentation, SCM sport, SCM port, scm_print_state *pstate));
289static void
290display_frame_expr (hdr, exp, tlr, indentation, sport, port, pstate)
291 char *hdr;
292 SCM exp;
293 char *tlr;
294 int indentation;
295 SCM sport;
296 SCM port;
297 scm_print_state *pstate;
298{
ab4f3efb
MD
299 if (SCM_NIMP (exp) && SCM_CONSP (exp))
300 {
301 scm_iprlist (hdr, exp, tlr[0], port, pstate);
b7f3516f 302 scm_puts (&tlr[1], port);
ab4f3efb
MD
303 }
304 else
305 scm_iprin1 (exp, port, pstate);
b7f3516f 306 scm_putc ('\n', port);
ab4f3efb
MD
307}
308
e3c37929
MD
309static void display_application SCM_P ((SCM frame, int indentation, SCM sport, SCM port, scm_print_state *pstate));
310static void
311display_application (frame, indentation, sport, port, pstate)
312 SCM frame;
313 int indentation;
314 SCM sport;
315 SCM port;
316 scm_print_state *pstate;
317{
318 SCM proc = SCM_FRAME_PROC (frame);
319 SCM name = (SCM_NFALSEP (scm_procedure_p (proc))
320 ? scm_procedure_name (proc)
321 : SCM_BOOL_F);
322 display_frame_expr ("[",
323 scm_cons (SCM_NFALSEP (name) ? name : proc,
324 SCM_FRAME_ARGS (frame)),
325 SCM_FRAME_EVAL_ARGS_P (frame) ? " ..." : "]",
326 indentation,
327 sport,
328 port,
329 pstate);
330}
331
332SCM_PROC(s_display_application, "display-application", 1, 1, 0, scm_display_application);
333
334SCM
335scm_display_application (SCM frame, SCM port)
336{
337 if (SCM_UNBNDP (port))
338 port = scm_cur_outp;
339 if (SCM_FRAME_PROC_P (frame))
340 /* Display an application. */
341 {
342 SCM print_state;
343 scm_print_state *pstate;
344
345 /* Create a print state for printing of frames. */
346 print_state = scm_make_print_state ();
347 pstate = SCM_PRINT_STATE (print_state);
348 pstate->writingp = 1;
349 pstate->fancyp = 1;
350 pstate->level = 2;
351 pstate->length = 9;
352
353 display_application (frame, 0, SCM_BOOL_F, port, pstate); /*fixme*/
354 return SCM_BOOL_T;
355 }
356 else
357 return SCM_BOOL_F;
358}
359
ab4f3efb
MD
360static void display_frame SCM_P ((SCM frame, int nfield, int indentation, SCM sport, SCM port, scm_print_state *pstate));
361static void
362display_frame (frame, nfield, indentation, sport, port, pstate)
363 SCM frame;
364 int nfield;
365 int indentation;
366 SCM sport;
367 SCM port;
368 scm_print_state *pstate;
369{
370 int n, i, j;
371
372 /* Announce missing frames? */
373 if (!SCM_BACKWARDS_P && SCM_FRAME_OVERFLOW_P (frame))
374 {
375 indent (nfield + 1 + indentation, port);
b7f3516f 376 scm_puts ("...\n", port);
ab4f3efb
MD
377 }
378
379 /* Check size of frame number. */
380 n = SCM_FRAME_NUMBER (frame);
381 for (i = 0, j = n; j > 0; ++i) j /= 10;
382
383 /* Number indentation. */
384 indent (nfield - (i ? i : 1), port);
385
386 /* Frame number. */
387 scm_iprin1 (SCM_MAKINUM (n), port, pstate);
388
389 /* Real frame marker */
b7f3516f 390 scm_putc (SCM_FRAME_REAL_P (frame) ? '*' : ' ', port);
ab4f3efb
MD
391
392 /* Indentation. */
393 indent (indentation, port);
394
395 if (SCM_FRAME_PROC_P (frame))
396 /* Display an application. */
e3c37929 397 display_application (frame, nfield + 1 + indentation, sport, port, pstate);
ab4f3efb
MD
398 else
399 /* Display a special form. */
400 {
401 SCM source = SCM_FRAME_SOURCE (frame);
2e9d6c6d 402 SCM copy = (SCM_NIMP (source) && SCM_CONSP (source)
7f2d92b1 403 ? scm_source_property (source, scm_sym_copy)
2e9d6c6d
MD
404 : SCM_BOOL_F);
405 SCM umcopy = (SCM_NIMP (source) && SCM_MEMOIZEDP (source)
406 ? scm_unmemoize (source)
407 : SCM_BOOL_F);
ab4f3efb 408 display_frame_expr ("(",
2e9d6c6d 409 SCM_NIMP (copy) && SCM_CONSP (copy) ? copy : umcopy,
ab4f3efb
MD
410 ")",
411 nfield + 1 + indentation,
412 sport,
413 port,
414 pstate);
415 }
416
417 /* Announce missing frames? */
418 if (SCM_BACKWARDS_P && SCM_FRAME_OVERFLOW_P (frame))
419 {
420 indent (nfield + 1 + indentation, port);
b7f3516f 421 scm_puts ("...\n", port);
ab4f3efb
MD
422 }
423}
424
bdf8afff
MD
425struct display_backtrace_args {
426 SCM stack;
427 SCM port;
428 SCM first;
429 SCM depth;
430};
431
ab4f3efb 432SCM_PROC(s_display_backtrace, "display-backtrace", 2, 2, 0, scm_display_backtrace);
bdf8afff
MD
433
434static SCM
39752bec 435display_backtrace_body (struct display_backtrace_args *a)
ab4f3efb
MD
436{
437 int n_frames, beg, end, n, i, j;
438 int nfield, indent_p, indentation;
439 SCM frame, sport, print_state;
440 scm_print_state *pstate;
441
78446828
MV
442 a->port = SCM_COERCE_OUTPORT (a->port);
443
ab4f3efb 444 /* Argument checking and extraction. */
bdf8afff
MD
445 SCM_ASSERT (SCM_NIMP (a->stack) && SCM_STACKP (a->stack),
446 a->stack,
ab4f3efb
MD
447 SCM_ARG1,
448 s_display_backtrace);
bdf8afff
MD
449 SCM_ASSERT (SCM_NIMP (a->port) && SCM_OPOUTPORTP (a->port),
450 a->port,
ab4f3efb
MD
451 SCM_ARG2,
452 s_display_backtrace);
bdf8afff
MD
453 n_frames = SCM_INUM (scm_stack_length (a->stack));
454 n = SCM_INUMP (a->depth) ? SCM_INUM (a->depth) : SCM_BACKTRACE_DEPTH;
ab4f3efb
MD
455 if (SCM_BACKWARDS_P)
456 {
bdf8afff 457 beg = SCM_INUMP (a->first) ? SCM_INUM (a->first) : 0;
ab4f3efb
MD
458 end = beg + n - 1;
459 if (end >= n_frames)
460 end = n_frames - 1;
461 n = end - beg + 1;
462 }
463 else
464 {
bdf8afff 465 if (SCM_INUMP (a->first))
ab4f3efb 466 {
bdf8afff 467 beg = SCM_INUM (a->first);
ab4f3efb
MD
468 end = beg - n + 1;
469 if (end < 0)
470 end = 0;
471 }
472 else
473 {
474 beg = n - 1;
475 end = 0;
476 if (beg >= n_frames)
477 beg = n_frames - 1;
478 }
479 n = beg - end + 1;
480 }
bdf8afff
MD
481 SCM_ASSERT (beg >= 0 && beg < n_frames, a->first, SCM_ARG3, s_display_backtrace);
482 SCM_ASSERT (n > 0, a->depth, SCM_ARG4, s_display_backtrace);
ab4f3efb
MD
483
484 /* Create a string port used for adaptation of printing parameters. */
485 sport = scm_mkstrport (SCM_INUM0,
486 scm_make_string (SCM_MAKINUM (240), SCM_UNDEFINED),
487 SCM_OPN | SCM_WRTNG,
488 s_display_backtrace);
489
490 /* Create a print state for printing of frames. */
491 print_state = scm_make_print_state ();
492 pstate = SCM_PRINT_STATE (print_state);
493 pstate->writingp = 1;
494 pstate->fancyp = 1;
e3c37929
MD
495 pstate->level = 2;
496 pstate->length = 3;
ab4f3efb
MD
497
498 /* First find out if it's reasonable to do indentation. */
499 if (SCM_BACKWARDS_P)
500 indent_p = 0;
501 else
502 {
503 indent_p = 1;
bdf8afff 504 frame = scm_stack_ref (a->stack, SCM_MAKINUM (beg));
ab4f3efb
MD
505 for (i = 0, j = 0; i < n; ++i)
506 {
507 if (SCM_FRAME_REAL_P (frame))
508 ++j;
509 if (j > SCM_BACKTRACE_INDENT)
510 {
511 indent_p = 0;
512 break;
513 }
514 frame = (SCM_BACKWARDS_P
515 ? SCM_FRAME_PREV (frame)
516 : SCM_FRAME_NEXT (frame));
517 }
518 }
519
520 /* Determine size of frame number field. */
bdf8afff 521 j = SCM_FRAME_NUMBER (scm_stack_ref (a->stack, SCM_MAKINUM (end)));
ab4f3efb
MD
522 for (i = 0; j > 0; ++i) j /= 10;
523 nfield = i ? i : 1;
524
b7f3516f 525 scm_puts ("Backtrace:\n", a->port);
ab4f3efb
MD
526
527 /* Print frames. */
bdf8afff 528 frame = scm_stack_ref (a->stack, SCM_MAKINUM (beg));
ab4f3efb 529 indentation = 1;
bdf8afff 530 display_frame (frame, nfield, indentation, sport, a->port, pstate);
ab4f3efb
MD
531 for (i = 1; i < n; ++i)
532 {
533 if (indent_p && SCM_FRAME_EVAL_ARGS_P (frame))
534 ++indentation;
535 frame = SCM_BACKWARDS_P ? SCM_FRAME_PREV (frame) : SCM_FRAME_NEXT (frame);
bdf8afff 536 display_frame (frame, nfield, indentation, sport, a->port, pstate);
ab4f3efb 537 }
bdf8afff
MD
538
539 return SCM_UNSPECIFIED;
540}
541
542SCM
543scm_display_backtrace (stack, port, first, depth)
544 SCM stack;
545 SCM port;
546 SCM first;
547 SCM depth;
548{
0b2cb4ee
MD
549 struct display_backtrace_args a;
550 struct display_error_handler_data data;
551 a.stack = stack;
552 a.port = port;
553 a.first = first;
554 a.depth = depth;
555 data.mode = "backtrace";
556 data.port = port;
bdf8afff
MD
557 scm_internal_catch (SCM_BOOL_T,
558 (scm_catch_body_t) display_backtrace_body, &a,
559 (scm_catch_handler_t) display_error_handler, &data);
ab4f3efb
MD
560 return SCM_UNSPECIFIED;
561}
562
78f9f47b 563SCM_VCELL (scm_has_shown_backtrace_hint_p_var, "has-shown-backtrace-hint?");
5aab5d96
MD
564
565SCM_PROC(s_backtrace, "backtrace", 0, 0, 0, scm_backtrace);
566SCM
567scm_backtrace ()
568{
b6609fc7 569 SCM the_last_stack = scm_fluid_ref (SCM_CDR (scm_the_last_stack_fluid));
a5d6d578 570 if (SCM_NFALSEP (the_last_stack))
5aab5d96
MD
571 {
572 scm_newline (scm_cur_outp);
a5d6d578 573 scm_display_backtrace (the_last_stack,
5aab5d96
MD
574 scm_cur_outp,
575 SCM_UNDEFINED,
576 SCM_UNDEFINED);
577 scm_newline (scm_cur_outp);
578 if (SCM_FALSEP (SCM_CDR (scm_has_shown_backtrace_hint_p_var))
579 && !SCM_BACKTRACE_P)
580 {
b7f3516f
TT
581 scm_puts ("Type \"(debug-enable 'backtrace)\" if you would like "
582 "a backtrace\n"
583 "automatically if an error occurs in the future.\n",
584 scm_cur_outp);
5aab5d96
MD
585 SCM_SETCDR (scm_has_shown_backtrace_hint_p_var, SCM_BOOL_T);
586 }
587 }
588 else
589 {
b7f3516f 590 scm_puts ("No backtrace available.\n", scm_cur_outp);
5aab5d96
MD
591 }
592 return SCM_UNSPECIFIED;
593}
594
ab4f3efb
MD
595\f
596
597void
598scm_init_backtrace ()
599{
a5d6d578 600 SCM f = scm_make_fluid ();
b6609fc7 601 scm_the_last_stack_fluid = scm_sysintern ("the-last-stack", f);
5aab5d96 602
ab4f3efb
MD
603#include "backtrace.x"
604}