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