backtrace.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /* Printing of backtraces and error messages
  2. * Copyright (C) 1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2009, 2010, 2011 Free Software Foundation
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public License
  6. * as published by the Free Software Foundation; either version 3 of
  7. * the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301 USA
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include "libguile/_scm.h"
  25. #ifdef HAVE_UNISTD_H
  26. #include <unistd.h>
  27. #endif
  28. #ifdef HAVE_IO_H
  29. #include <io.h>
  30. #endif
  31. #include "libguile/deprecation.h"
  32. #include "libguile/stacks.h"
  33. #include "libguile/srcprop.h"
  34. #include "libguile/struct.h"
  35. #include "libguile/strports.h"
  36. #include "libguile/throw.h"
  37. #include "libguile/fluids.h"
  38. #include "libguile/ports.h"
  39. #include "libguile/strings.h"
  40. #include "libguile/dynwind.h"
  41. #include "libguile/frames.h"
  42. #include "libguile/validate.h"
  43. #include "libguile/backtrace.h"
  44. #include "libguile/filesys.h"
  45. #include "libguile/private-options.h"
  46. /* {Error reporting and backtraces}
  47. *
  48. * Note that these functions shouldn't generate errors themselves.
  49. */
  50. static SCM
  51. boot_print_exception (SCM port, SCM frame, SCM key, SCM args)
  52. #define FUNC_NAME "boot-print-exception"
  53. {
  54. scm_puts_unlocked ("Throw to key ", port);
  55. scm_write (key, port);
  56. scm_puts_unlocked (" with args ", port);
  57. scm_write (args, port);
  58. return SCM_UNSPECIFIED;
  59. }
  60. #undef FUNC_NAME
  61. SCM
  62. scm_print_exception (SCM port, SCM frame, SCM key, SCM args)
  63. #define FUNC_NAME "print-exception"
  64. {
  65. static SCM print_exception = SCM_BOOL_F;
  66. SCM_VALIDATE_OPOUTPORT (1, port);
  67. if (scm_is_true (frame))
  68. SCM_VALIDATE_FRAME (2, frame);
  69. SCM_VALIDATE_SYMBOL (3, key);
  70. SCM_VALIDATE_LIST (4, args);
  71. if (scm_is_false (print_exception))
  72. print_exception =
  73. scm_module_variable (scm_the_root_module (),
  74. scm_from_latin1_symbol ("print-exception"));
  75. return scm_call_4 (scm_variable_ref (print_exception),
  76. port, frame, key, args);
  77. }
  78. #undef FUNC_NAME
  79. /* Print parameters for error messages. */
  80. #define DISPLAY_ERROR_MESSAGE_MAX_LEVEL 7
  81. #define DISPLAY_ERROR_MESSAGE_MAX_LENGTH 10
  82. /* Print parameters for failing expressions in error messages.
  83. * (See also `print_params' below for backtrace print parameters.)
  84. */
  85. #define DISPLAY_EXPRESSION_MAX_LEVEL 2
  86. #define DISPLAY_EXPRESSION_MAX_LENGTH 3
  87. #undef SCM_ASSERT
  88. #define SCM_ASSERT(_cond, _arg, _pos, _subr) \
  89. if (!(_cond)) \
  90. return SCM_BOOL_F;
  91. void
  92. scm_display_error_message (SCM message, SCM args, SCM port)
  93. {
  94. scm_print_exception (port, SCM_BOOL_F, scm_misc_error_key,
  95. scm_list_3 (SCM_BOOL_F, message, args));
  96. }
  97. /* The function scm_i_display_error prints out a detailed error message. This
  98. * function will be called directly within libguile to signal error messages.
  99. * No parameter checks will be performed by scm_i_display_error. Thus, User
  100. * code should rather use the function scm_display_error.
  101. */
  102. void
  103. scm_i_display_error (SCM frame, SCM port, SCM subr, SCM message, SCM args, SCM rest)
  104. {
  105. scm_print_exception (port, frame, scm_misc_error_key,
  106. scm_list_3 (subr, message, args));
  107. }
  108. SCM_DEFINE (scm_display_error, "display-error", 6, 0, 0,
  109. (SCM frame, SCM port, SCM subr, SCM message, SCM args, SCM rest),
  110. "Display an error message to the output port @var{port}.\n"
  111. "@var{frame} is the frame in which the error occurred, @var{subr} is\n"
  112. "the name of the procedure in which the error occurred and\n"
  113. "@var{message} is the actual error message, which may contain\n"
  114. "formatting instructions. These will format the arguments in\n"
  115. "the list @var{args} accordingly. @var{rest} is currently\n"
  116. "ignored.")
  117. #define FUNC_NAME s_scm_display_error
  118. {
  119. SCM_VALIDATE_OUTPUT_PORT (2, port);
  120. scm_i_display_error (frame, port, subr, message, args, rest);
  121. return SCM_UNSPECIFIED;
  122. }
  123. #undef FUNC_NAME
  124. typedef struct {
  125. int level;
  126. int length;
  127. } print_params_t;
  128. static int n_print_params = 9;
  129. static print_params_t default_print_params[] = {
  130. { 4, 9 }, { 4, 3 },
  131. { 3, 4 }, { 3, 3 },
  132. { 2, 4 }, { 2, 3 },
  133. { 1, 4 }, { 1, 3 }, { 1, 2 }
  134. };
  135. static print_params_t *print_params = default_print_params;
  136. #ifdef GUILE_DEBUG
  137. SCM_DEFINE (scm_set_print_params_x, "set-print-params!", 1, 0, 0,
  138. (SCM params),
  139. "Set the print parameters to the values from @var{params}.\n"
  140. "@var{params} must be a list of two-element lists which must\n"
  141. "hold two integer values.")
  142. #define FUNC_NAME s_scm_set_print_params_x
  143. {
  144. int i;
  145. int n;
  146. SCM ls;
  147. print_params_t *new_params;
  148. SCM_VALIDATE_NONEMPTYLIST_COPYLEN (2, params, n);
  149. for (ls = params; !SCM_NULL_OR_NIL_P (ls); ls = SCM_CDR (ls))
  150. SCM_ASSERT (scm_ilength (SCM_CAR (params)) == 2
  151. && scm_is_unsigned_integer (SCM_CAAR (ls), 0, INT_MAX)
  152. && scm_is_unsigned_integer (SCM_CADAR (ls), 0, INT_MAX),
  153. params,
  154. SCM_ARG2,
  155. s_scm_set_print_params_x);
  156. new_params = scm_malloc (n * sizeof (print_params_t));
  157. if (print_params != default_print_params)
  158. free (print_params);
  159. print_params = new_params;
  160. for (i = 0; i < n; ++i)
  161. {
  162. print_params[i].level = scm_to_int (SCM_CAAR (params));
  163. print_params[i].length = scm_to_int (SCM_CADAR (params));
  164. params = SCM_CDR (params);
  165. }
  166. n_print_params = n;
  167. return SCM_UNSPECIFIED;
  168. }
  169. #undef FUNC_NAME
  170. #endif
  171. static void
  172. indent (int n, SCM port)
  173. {
  174. int i;
  175. for (i = 0; i < n; ++i)
  176. scm_putc_unlocked (' ', port);
  177. }
  178. static void
  179. display_frame_expr (char *hdr, SCM exp, char *tlr, int indentation, SCM sport, SCM port, scm_print_state *pstate)
  180. {
  181. int i = 0, n;
  182. scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (sport);
  183. do
  184. {
  185. pstate->length = print_params[i].length;
  186. ptob->seek (sport, 0, SEEK_SET);
  187. if (scm_is_pair (exp))
  188. {
  189. pstate->level = print_params[i].level - 1;
  190. scm_iprlist (hdr, exp, tlr[0], sport, pstate);
  191. scm_puts_unlocked (&tlr[1], sport);
  192. }
  193. else
  194. {
  195. pstate->level = print_params[i].level;
  196. scm_iprin1 (exp, sport, pstate);
  197. }
  198. ptob->flush (sport);
  199. n = ptob->seek (sport, 0, SEEK_CUR);
  200. ++i;
  201. }
  202. while (indentation + n > SCM_BACKTRACE_WIDTH && i < n_print_params);
  203. ptob->truncate (sport, n);
  204. scm_display (scm_strport_to_string (sport), port);
  205. }
  206. static void
  207. display_application (SCM frame, int indentation, SCM sport, SCM port, scm_print_state *pstate)
  208. {
  209. SCM proc = scm_frame_procedure (frame);
  210. SCM name = (scm_is_true (scm_procedure_p (proc))
  211. ? scm_procedure_name (proc)
  212. : SCM_BOOL_F);
  213. display_frame_expr ("[",
  214. scm_cons (scm_is_true (name) ? name : proc,
  215. scm_frame_arguments (frame)),
  216. "]",
  217. indentation,
  218. sport,
  219. port,
  220. pstate);
  221. }
  222. SCM_DEFINE (scm_display_application, "display-application", 1, 2, 0,
  223. (SCM frame, SCM port, SCM indent),
  224. "Display a procedure application @var{frame} to the output port\n"
  225. "@var{port}. @var{indent} specifies the indentation of the\n"
  226. "output.")
  227. #define FUNC_NAME s_scm_display_application
  228. {
  229. SCM_VALIDATE_FRAME (1, frame);
  230. if (SCM_UNBNDP (port))
  231. port = scm_current_output_port ();
  232. else
  233. SCM_VALIDATE_OPOUTPORT (2, port);
  234. if (SCM_UNBNDP (indent))
  235. indent = SCM_INUM0;
  236. /* Display an application. */
  237. {
  238. SCM sport, print_state;
  239. scm_print_state *pstate;
  240. /* Create a string port used for adaptation of printing parameters. */
  241. sport = scm_mkstrport (SCM_INUM0, SCM_BOOL_F,
  242. SCM_OPN | SCM_WRTNG,
  243. FUNC_NAME);
  244. /* Create a print state for printing of frames. */
  245. print_state = scm_make_print_state ();
  246. pstate = SCM_PRINT_STATE (print_state);
  247. pstate->writingp = 1;
  248. pstate->fancyp = 1;
  249. display_application (frame, scm_to_int (indent), sport, port, pstate);
  250. return SCM_BOOL_T;
  251. }
  252. }
  253. #undef FUNC_NAME
  254. SCM_SYMBOL (sym_base, "base");
  255. static void
  256. display_backtrace_get_file_line (SCM frame, SCM *file, SCM *line)
  257. {
  258. SCM source = scm_frame_source (frame);
  259. *file = *line = SCM_BOOL_F;
  260. if (scm_is_pair (source)
  261. && scm_is_pair (scm_cdr (source))
  262. && scm_is_pair (scm_cddr (source))
  263. && !scm_is_pair (scm_cdddr (source)))
  264. {
  265. /* (addr . (filename . (line . column))), from vm compilation */
  266. *file = scm_cadr (source);
  267. *line = scm_caddr (source);
  268. }
  269. }
  270. static void
  271. display_backtrace_file (frame, last_file, port, pstate)
  272. SCM frame;
  273. SCM *last_file;
  274. SCM port;
  275. scm_print_state *pstate;
  276. {
  277. SCM file, line;
  278. display_backtrace_get_file_line (frame, &file, &line);
  279. if (scm_is_true (scm_equal_p (file, *last_file)))
  280. return;
  281. *last_file = file;
  282. scm_puts_unlocked ("In ", port);
  283. if (scm_is_false (file))
  284. if (scm_is_false (line))
  285. scm_puts_unlocked ("unknown file", port);
  286. else
  287. scm_puts_unlocked ("current input", port);
  288. else
  289. {
  290. pstate->writingp = 0;
  291. scm_iprin1 (file, port, pstate);
  292. pstate->writingp = 1;
  293. }
  294. scm_puts_unlocked (":\n", port);
  295. }
  296. static void
  297. display_backtrace_file_and_line (SCM frame, SCM port, scm_print_state *pstate)
  298. {
  299. SCM file, line;
  300. display_backtrace_get_file_line (frame, &file, &line);
  301. if (scm_is_eq (SCM_PACK (SCM_SHOW_FILE_NAME), sym_base))
  302. {
  303. if (scm_is_false (file))
  304. {
  305. if (scm_is_false (line))
  306. scm_putc_unlocked ('?', port);
  307. else
  308. scm_puts_unlocked ("<stdin>", port);
  309. }
  310. else
  311. {
  312. pstate -> writingp = 0;
  313. #ifdef HAVE_POSIX
  314. scm_iprin1 ((scm_is_string (file)?
  315. scm_basename (file, SCM_UNDEFINED) : file),
  316. port, pstate);
  317. #else
  318. scm_iprin1 (file, port, pstate);
  319. #endif
  320. pstate -> writingp = 1;
  321. }
  322. scm_putc_unlocked (':', port);
  323. }
  324. else if (scm_is_true (line))
  325. {
  326. int i, j=0;
  327. for (i = scm_to_int (line)+1; i > 0; i = i/10, j++)
  328. ;
  329. indent (4-j, port);
  330. }
  331. if (scm_is_false (line))
  332. scm_puts_unlocked (" ?", port);
  333. else
  334. scm_intprint (scm_to_int (line) + 1, 10, port);
  335. scm_puts_unlocked (": ", port);
  336. }
  337. static void
  338. display_frame (SCM frame, int n, int nfield, int indentation,
  339. SCM sport, SCM port, scm_print_state *pstate)
  340. {
  341. int i, j;
  342. /* display file name and line number */
  343. if (scm_is_true (SCM_PACK (SCM_SHOW_FILE_NAME)))
  344. display_backtrace_file_and_line (frame, port, pstate);
  345. /* Check size of frame number. */
  346. for (i = 0, j = n; j > 0; ++i) j /= 10;
  347. /* Number indentation. */
  348. indent (nfield - (i ? i : 1), port);
  349. /* Frame number. */
  350. scm_iprin1 (scm_from_int (n), port, pstate);
  351. /* Indentation. */
  352. indent (indentation, port);
  353. /* Display an application. */
  354. display_application (frame, nfield + 1 + indentation, sport, port, pstate);
  355. scm_putc_unlocked ('\n', port);
  356. }
  357. struct display_backtrace_args {
  358. SCM stack;
  359. SCM port;
  360. SCM first;
  361. SCM depth;
  362. SCM highlight_objects;
  363. };
  364. static SCM
  365. display_backtrace_body (struct display_backtrace_args *a)
  366. #define FUNC_NAME "display_backtrace_body"
  367. {
  368. int n_frames, beg, end, n, i, j;
  369. int nfield, indentation;
  370. SCM frame, sport, print_state;
  371. SCM last_file;
  372. scm_print_state *pstate;
  373. a->port = SCM_COERCE_OUTPORT (a->port);
  374. /* Argument checking and extraction. */
  375. SCM_VALIDATE_STACK (1, a->stack);
  376. SCM_VALIDATE_OPOUTPORT (2, a->port);
  377. n_frames = scm_to_int (scm_stack_length (a->stack));
  378. n = scm_is_integer (a->depth) ? scm_to_int (a->depth) : SCM_BACKTRACE_DEPTH;
  379. if (SCM_BACKWARDS_P)
  380. {
  381. beg = scm_is_integer (a->first) ? scm_to_int (a->first) : 0;
  382. end = beg + n - 1;
  383. if (end >= n_frames)
  384. end = n_frames - 1;
  385. n = end - beg + 1;
  386. }
  387. else
  388. {
  389. if (scm_is_integer (a->first))
  390. {
  391. beg = scm_to_int (a->first);
  392. end = beg - n + 1;
  393. if (end < 0)
  394. end = 0;
  395. }
  396. else
  397. {
  398. beg = n - 1;
  399. end = 0;
  400. if (beg >= n_frames)
  401. beg = n_frames - 1;
  402. }
  403. n = beg - end + 1;
  404. }
  405. SCM_ASSERT (beg >= 0 && beg < n_frames, a->first, SCM_ARG3, s_display_backtrace);
  406. SCM_ASSERT (n > 0, a->depth, SCM_ARG4, s_display_backtrace);
  407. /* Create a string port used for adaptation of printing parameters. */
  408. sport = scm_mkstrport (SCM_INUM0, SCM_BOOL_F,
  409. SCM_OPN | SCM_WRTNG,
  410. FUNC_NAME);
  411. /* Create a print state for printing of frames. */
  412. print_state = scm_make_print_state ();
  413. pstate = SCM_PRINT_STATE (print_state);
  414. pstate->writingp = 1;
  415. pstate->fancyp = 1;
  416. pstate->highlight_objects = a->highlight_objects;
  417. /* Determine size of frame number field. */
  418. j = end;
  419. for (i = 0; j > 0; ++i) j /= 10;
  420. nfield = i ? i : 1;
  421. /* Print frames. */
  422. indentation = 1;
  423. last_file = SCM_UNDEFINED;
  424. if (SCM_BACKWARDS_P)
  425. end++;
  426. else
  427. end--;
  428. for (i = beg; i != end; SCM_BACKWARDS_P ? ++i : --i)
  429. {
  430. frame = scm_stack_ref (a->stack, scm_from_int (i));
  431. if (!scm_is_eq (SCM_PACK (SCM_SHOW_FILE_NAME), sym_base))
  432. display_backtrace_file (frame, &last_file, a->port, pstate);
  433. display_frame (frame, i, nfield, indentation, sport, a->port, pstate);
  434. }
  435. scm_remember_upto_here_1 (print_state);
  436. return SCM_UNSPECIFIED;
  437. }
  438. #undef FUNC_NAME
  439. static SCM
  440. error_during_backtrace (void *data, SCM tag, SCM throw_args)
  441. {
  442. SCM port = SCM_PACK_POINTER (data);
  443. scm_puts_unlocked ("Exception thrown while printing backtrace:\n", port);
  444. scm_print_exception (port, SCM_BOOL_F, tag, throw_args);
  445. return SCM_UNSPECIFIED;
  446. }
  447. SCM_DEFINE (scm_display_backtrace_with_highlights, "display-backtrace", 2, 3, 0,
  448. (SCM stack, SCM port, SCM first, SCM depth, SCM highlights),
  449. "Display a backtrace to the output port @var{port}. @var{stack}\n"
  450. "is the stack to take the backtrace from, @var{first} specifies\n"
  451. "where in the stack to start and @var{depth} how many frames\n"
  452. "to display. @var{first} and @var{depth} can be @code{#f},\n"
  453. "which means that default values will be used.\n"
  454. "If @var{highlights} is given it should be a list; the elements\n"
  455. "of this list will be highlighted wherever they appear in the\n"
  456. "backtrace.")
  457. #define FUNC_NAME s_scm_display_backtrace_with_highlights
  458. {
  459. struct display_backtrace_args a;
  460. a.stack = stack;
  461. a.port = port;
  462. a.first = first;
  463. a.depth = depth;
  464. if (SCM_UNBNDP (highlights))
  465. a.highlight_objects = SCM_EOL;
  466. else
  467. a.highlight_objects = highlights;
  468. scm_internal_catch (SCM_BOOL_T,
  469. (scm_t_catch_body) display_backtrace_body, &a,
  470. (scm_t_catch_handler) error_during_backtrace, SCM_UNPACK_POINTER (port));
  471. return SCM_UNSPECIFIED;
  472. }
  473. #undef FUNC_NAME
  474. SCM
  475. scm_display_backtrace (SCM stack, SCM port, SCM first, SCM depth)
  476. {
  477. return scm_display_backtrace_with_highlights (stack, port, first, depth,
  478. SCM_EOL);
  479. }
  480. SCM_VARIABLE (scm_has_shown_backtrace_hint_p_var, "has-shown-backtrace-hint?");
  481. SCM_DEFINE (scm_backtrace_with_highlights, "backtrace", 0, 1, 0,
  482. (SCM highlights),
  483. "Display a backtrace of the current stack to the current\n"
  484. "output port. If @var{highlights} is given, it should be\n"
  485. "a list; the elements of this list will be highlighted\n"
  486. "wherever they appear in the backtrace.")
  487. #define FUNC_NAME s_scm_backtrace_with_highlights
  488. {
  489. SCM port = scm_current_output_port ();
  490. SCM stack = scm_make_stack (SCM_BOOL_T, SCM_EOL);
  491. if (SCM_UNBNDP (highlights))
  492. highlights = SCM_EOL;
  493. scm_newline (port);
  494. scm_puts_unlocked ("Backtrace:\n", port);
  495. scm_display_backtrace_with_highlights (stack, port, SCM_BOOL_F, SCM_BOOL_F,
  496. highlights);
  497. scm_newline (port);
  498. return SCM_UNSPECIFIED;
  499. }
  500. #undef FUNC_NAME
  501. SCM
  502. scm_backtrace (void)
  503. {
  504. return scm_backtrace_with_highlights (SCM_EOL);
  505. }
  506. void
  507. scm_init_backtrace ()
  508. {
  509. scm_c_define_gsubr ("print-exception", 4, 0, 0, boot_print_exception);
  510. #include "libguile/backtrace.x"
  511. }
  512. /*
  513. Local Variables:
  514. c-file-style: "gnu"
  515. End:
  516. */