1/* GDB/Scheme exception support.
2
3   Copyright (C) 2014-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20/* See README file in this directory for implementation notes, coding
21   conventions, et.al.  */
22
23/* Notes:
24
25   IWBN to support SRFI 34/35.  At the moment we follow Guile's own
26   exception mechanism.
27
28   The non-static functions in this file have prefix gdbscm_ and
29   not exscm_ on purpose.  */
30
31#include "defs.h"
32#include <signal.h>
33#include "guile-internal.h"
34
35/* The <gdb:exception> smob.
36   This is used to record and handle Scheme exceptions.
37   One important invariant is that <gdb:exception> smobs are never a valid
38   result of a function, other than to signify an exception occurred.  */
39
40typedef struct
41{
42  /* This always appears first.  */
43  gdb_smob base;
44
45  /* The key and args parameters to "throw".  */
46  SCM key;
47  SCM args;
48} exception_smob;
49
50static const char exception_smob_name[] = "gdb:exception";
51
52/* The tag Guile knows the exception smob by.  */
53static scm_t_bits exception_smob_tag;
54
55/* A generic error in struct gdb_exception.
56   I.e., not RETURN_QUIT and not MEMORY_ERROR.  */
57static SCM error_symbol;
58
59/* An error occurred accessing inferior memory.
60   This is not a Scheme programming error.  */
61static SCM memory_error_symbol;
62
63/* User interrupt, e.g., RETURN_QUIT in struct gdb_exception.  */
64static SCM signal_symbol;
65
66/* A user error, e.g., bad arg to gdb command.  */
67static SCM user_error_symbol;
68
69/* Printing the stack is done by first capturing the stack and recording it in
70   a <gdb:exception> object with this key and with the ARGS field set to
71   (cons real-key (cons stack real-args)).
72   See gdbscm_make_exception_with_stack.  */
73static SCM with_stack_error_symbol;
74
75/* The key to use for an invalid object exception.  An invalid object is one
76   where the underlying object has been removed from GDB.  */
77SCM gdbscm_invalid_object_error_symbol;
78
79/* Values for "guile print-stack" as symbols.  */
80static SCM none_symbol;
81static SCM message_symbol;
82static SCM full_symbol;
83
84static const char percent_print_exception_message_name[] =
85  "%print-exception-message";
86
87/* Variable containing %print-exception-message.
88   It is not defined until late in initialization, after our init routine
89   has run.  Cope by looking it up lazily.  */
90static SCM percent_print_exception_message_var = SCM_BOOL_F;
91
92static const char percent_print_exception_with_stack_name[] =
93  "%print-exception-with-stack";
94
95/* Variable containing %print-exception-with-stack.
96   It is not defined until late in initialization, after our init routine
97   has run.  Cope by looking it up lazily.  */
98static SCM percent_print_exception_with_stack_var = SCM_BOOL_F;
99
100/* Counter to keep track of the number of times we create a <gdb:exception>
101   object, for performance monitoring purposes.  */
102static unsigned long gdbscm_exception_count = 0;
103
104/* Administrivia for exception smobs.  */
105
106/* The smob "print" function for <gdb:exception>.  */
107
108static int
109exscm_print_exception_smob (SCM self, SCM port, scm_print_state *pstate)
110{
111  exception_smob *e_smob = (exception_smob *) SCM_SMOB_DATA (self);
112
113  gdbscm_printf (port, "#<%s ", exception_smob_name);
114  scm_write (e_smob->key, port);
115  scm_puts (" ", port);
116  scm_write (e_smob->args, port);
117  scm_puts (">", port);
118
119  scm_remember_upto_here_1 (self);
120
121  /* Non-zero means success.  */
122  return 1;
123}
124
125/* (make-exception key args) -> <gdb:exception> */
126
127SCM
128gdbscm_make_exception (SCM key, SCM args)
129{
130  exception_smob *e_smob = (exception_smob *)
131    scm_gc_malloc (sizeof (exception_smob), exception_smob_name);
132  SCM smob;
133
134  e_smob->key = key;
135  e_smob->args = args;
136  smob = scm_new_smob (exception_smob_tag, (scm_t_bits) e_smob);
137  gdbscm_init_gsmob (&e_smob->base);
138
139  ++gdbscm_exception_count;
140
141  return smob;
142}
143
144/* Return non-zero if SCM is a <gdb:exception> object.  */
145
146int
147gdbscm_is_exception (SCM scm)
148{
149  return SCM_SMOB_PREDICATE (exception_smob_tag, scm);
150}
151
152/* (exception? scm) -> boolean */
153
154static SCM
155gdbscm_exception_p (SCM scm)
156{
157  return scm_from_bool (gdbscm_is_exception (scm));
158}
159
160/* (exception-key <gdb:exception>) -> key */
161
162SCM
163gdbscm_exception_key (SCM self)
164{
165  exception_smob *e_smob;
166
167  SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME,
168		   "gdb:exception");
169
170  e_smob = (exception_smob *) SCM_SMOB_DATA (self);
171  return e_smob->key;
172}
173
174/* (exception-args <gdb:exception>) -> arg-list */
175
176SCM
177gdbscm_exception_args (SCM self)
178{
179  exception_smob *e_smob;
180
181  SCM_ASSERT_TYPE (gdbscm_is_exception (self), self, SCM_ARG1, FUNC_NAME,
182		   "gdb:exception");
183
184  e_smob = (exception_smob *) SCM_SMOB_DATA (self);
185  return e_smob->args;
186}
187
188/* Wrap an exception in a <gdb:exception> object that includes STACK.
189   gdbscm_print_exception_with_stack knows how to unwrap it.  */
190
191SCM
192gdbscm_make_exception_with_stack (SCM key, SCM args, SCM stack)
193{
194  return gdbscm_make_exception (with_stack_error_symbol,
195				scm_cons (key, scm_cons (stack, args)));
196}
197
198/* Version of scm_error_scm that creates a gdb:exception object that can later
199   be passed to gdbscm_throw.
200   KEY is a symbol denoting the kind of error.
201   SUBR is either #f or a string marking the function in which the error
202   occurred.
203   MESSAGE is either #f or the error message string.  It may contain ~a and ~s
204   modifiers, provided by ARGS.
205   ARGS is a list of args to MESSAGE.
206   DATA is an arbitrary object, its value depends on KEY.  The value to pass
207   here is a bit underspecified by Guile.  */
208
209SCM
210gdbscm_make_error_scm (SCM key, SCM subr, SCM message, SCM args, SCM data)
211{
212  return gdbscm_make_exception (key, scm_list_4 (subr, message, args, data));
213}
214
215/* Version of scm_error that creates a gdb:exception object that can later
216   be passed to gdbscm_throw.
217   See gdbscm_make_error_scm for a description of the arguments.  */
218
219SCM
220gdbscm_make_error (SCM key, const char *subr, const char *message,
221		   SCM args, SCM data)
222{
223  return gdbscm_make_error_scm
224    (key,
225     subr == NULL ? SCM_BOOL_F : scm_from_latin1_string (subr),
226     message == NULL ? SCM_BOOL_F : scm_from_latin1_string (message),
227     args, data);
228}
229
230/* Version of SCM_ASSERT_TYPE/scm_wrong_type_arg_msg that creates a
231   gdb:exception object that can later be passed to gdbscm_throw.  */
232
233SCM
234gdbscm_make_type_error (const char *subr, int arg_pos, SCM bad_value,
235			const char *expected_type)
236{
237  char *msg;
238  SCM result;
239
240  if (arg_pos > 0)
241    {
242      if (expected_type != NULL)
243	{
244	  msg = xstrprintf (_("Wrong type argument in position %d"
245			      " (expecting %s): ~S"),
246			    arg_pos, expected_type);
247	}
248      else
249	{
250	  msg = xstrprintf (_("Wrong type argument in position %d: ~S"),
251			    arg_pos);
252	}
253    }
254  else
255    {
256      if (expected_type != NULL)
257	{
258	  msg = xstrprintf (_("Wrong type argument (expecting %s): ~S"),
259			    expected_type);
260	}
261      else
262	msg = xstrprintf (_("Wrong type argument: ~S"));
263    }
264
265  result = gdbscm_make_error (scm_arg_type_key, subr, msg,
266			      scm_list_1 (bad_value), scm_list_1 (bad_value));
267  xfree (msg);
268  return result;
269}
270
271/* A variant of gdbscm_make_type_error for non-type argument errors.
272   ERROR_PREFIX and ERROR are combined to build the error message.
273   Care needs to be taken so that the i18n composed form is still
274   reasonable, but no one is going to translate these anyway so we don't
275   worry too much.
276   ERROR_PREFIX may be NULL, ERROR may not be NULL.  */
277
278static SCM
279gdbscm_make_arg_error (SCM key, const char *subr, int arg_pos, SCM bad_value,
280		       const char *error_prefix, const char *error)
281{
282  char *msg;
283  SCM result;
284
285  if (error_prefix != NULL)
286    {
287      if (arg_pos > 0)
288	{
289	  msg = xstrprintf (_("%s %s in position %d: ~S"),
290			    error_prefix, error, arg_pos);
291	}
292      else
293	msg = xstrprintf (_("%s %s: ~S"), error_prefix, error);
294    }
295  else
296    {
297      if (arg_pos > 0)
298	msg = xstrprintf (_("%s in position %d: ~S"), error, arg_pos);
299      else
300	msg = xstrprintf (_("%s: ~S"), error);
301    }
302
303  result = gdbscm_make_error (key, subr, msg,
304			      scm_list_1 (bad_value), scm_list_1 (bad_value));
305  xfree (msg);
306  return result;
307}
308
309/* Make an invalid-object error <gdb:exception> object.
310   OBJECT is the name of the kind of object that is invalid.  */
311
312SCM
313gdbscm_make_invalid_object_error (const char *subr, int arg_pos, SCM bad_value,
314				  const char *object)
315{
316  return gdbscm_make_arg_error (gdbscm_invalid_object_error_symbol,
317				subr, arg_pos, bad_value,
318				_("Invalid object:"), object);
319}
320
321/* Throw an invalid-object error.
322   OBJECT is the name of the kind of object that is invalid.  */
323
324void
325gdbscm_invalid_object_error (const char *subr, int arg_pos, SCM bad_value,
326			     const char *object)
327{
328  SCM exception
329    = gdbscm_make_invalid_object_error (subr, arg_pos, bad_value, object);
330
331  gdbscm_throw (exception);
332}
333
334/* Make an out-of-range error <gdb:exception> object.  */
335
336SCM
337gdbscm_make_out_of_range_error (const char *subr, int arg_pos, SCM bad_value,
338				const char *error)
339{
340  return gdbscm_make_arg_error (scm_out_of_range_key,
341				subr, arg_pos, bad_value,
342				_("Out of range:"), error);
343}
344
345/* Throw an out-of-range error.
346   This is the standard Guile out-of-range exception.  */
347
348void
349gdbscm_out_of_range_error (const char *subr, int arg_pos, SCM bad_value,
350			   const char *error)
351{
352  SCM exception
353    = gdbscm_make_out_of_range_error (subr, arg_pos, bad_value, error);
354
355  gdbscm_throw (exception);
356}
357
358/* Make a misc-error <gdb:exception> object.  */
359
360SCM
361gdbscm_make_misc_error (const char *subr, int arg_pos, SCM bad_value,
362			const char *error)
363{
364  return gdbscm_make_arg_error (scm_misc_error_key,
365				subr, arg_pos, bad_value, NULL, error);
366}
367
368/* Throw a misc-error error.  */
369
370void
371gdbscm_misc_error (const char *subr, int arg_pos, SCM bad_value,
372		   const char *error)
373{
374  SCM exception = gdbscm_make_misc_error (subr, arg_pos, bad_value, error);
375
376  gdbscm_throw (exception);
377}
378
379/* Return a <gdb:exception> object for gdb:memory-error.  */
380
381SCM
382gdbscm_make_memory_error (const char *subr, const char *msg, SCM args)
383{
384  return gdbscm_make_error (memory_error_symbol, subr, msg, args,
385			    SCM_EOL);
386}
387
388/* Throw a gdb:memory-error exception.  */
389
390void
391gdbscm_memory_error (const char *subr, const char *msg, SCM args)
392{
393  SCM exception = gdbscm_make_memory_error (subr, msg, args);
394
395  gdbscm_throw (exception);
396}
397
398/* Return non-zero if KEY is gdb:memory-error.
399   Note: This is an excp_matcher_func function.  */
400
401int
402gdbscm_memory_error_p (SCM key)
403{
404  return scm_is_eq (key, memory_error_symbol);
405}
406
407/* Return non-zero if KEY is gdb:user-error.
408   Note: This is an excp_matcher_func function.  */
409
410int
411gdbscm_user_error_p (SCM key)
412{
413  return scm_is_eq (key, user_error_symbol);
414}
415
416/* Wrapper around scm_throw to throw a gdb:exception.
417   This function does not return.
418   This function cannot be called from inside TRY_CATCH.  */
419
420void
421gdbscm_throw (SCM exception)
422{
423  scm_throw (gdbscm_exception_key (exception),
424	     gdbscm_exception_args (exception));
425  gdb_assert_not_reached ("scm_throw returned");
426}
427
428/* Convert a GDB exception to a <gdb:exception> object.  */
429
430SCM
431gdbscm_scm_from_gdb_exception (const gdbscm_gdb_exception &exception)
432{
433  SCM key;
434
435  if (exception.reason == RETURN_QUIT)
436    {
437      /* Handle this specially to be consistent with top-repl.scm.  */
438      return gdbscm_make_error (signal_symbol, NULL, _("User interrupt"),
439				SCM_EOL, scm_list_1 (scm_from_int (SIGINT)));
440    }
441
442  if (exception.error == MEMORY_ERROR)
443    key = memory_error_symbol;
444  else
445    key = error_symbol;
446
447  return gdbscm_make_error (key, NULL, "~A",
448			    scm_list_1 (gdbscm_scm_from_c_string
449					(exception.message)),
450			    SCM_BOOL_F);
451}
452
453/* Convert a GDB exception to the appropriate Scheme exception and throw it.
454   This function does not return.  */
455
456void
457gdbscm_throw_gdb_exception (gdbscm_gdb_exception exception)
458{
459  SCM scm_exception = gdbscm_scm_from_gdb_exception (exception);
460  xfree (exception.message);
461  gdbscm_throw (scm_exception);
462}
463
464/* Print the error message portion of an exception.
465   If PORT is #f, use the standard error port.
466   KEY cannot be gdb:with-stack.
467
468   Basically this function is just a wrapper around calling
469   %print-exception-message.  */
470
471static void
472gdbscm_print_exception_message (SCM port, SCM frame, SCM key, SCM args)
473{
474  SCM printer, status;
475
476  if (gdbscm_is_false (port))
477    port = scm_current_error_port ();
478
479  gdb_assert (!scm_is_eq (key, with_stack_error_symbol));
480
481  /* This does not use scm_print_exception because we tweak the output a bit.
482     Compare Guile's print-exception with our %print-exception-message for
483     details.  */
484  if (gdbscm_is_false (percent_print_exception_message_var))
485    {
486      percent_print_exception_message_var
487	= scm_c_private_variable (gdbscm_init_module_name,
488				  percent_print_exception_message_name);
489      /* If we can't find %print-exception-message, there's a problem on the
490	 Scheme side.  Don't kill GDB, just flag an error and leave it at
491	 that.  */
492      if (gdbscm_is_false (percent_print_exception_message_var))
493	{
494	  gdbscm_printf (port, _("Error in Scheme exception printing,"
495				 " can't find %s.\n"),
496			 percent_print_exception_message_name);
497	  return;
498	}
499    }
500  printer = scm_variable_ref (percent_print_exception_message_var);
501
502  status = gdbscm_safe_call_4 (printer, port, frame, key, args, NULL);
503
504  /* If that failed still tell the user something.
505     But don't use the exception printing machinery!  */
506  if (gdbscm_is_exception (status))
507    {
508      gdbscm_printf (port, _("Error in Scheme exception printing:\n"));
509      scm_display (status, port);
510      scm_newline (port);
511    }
512}
513
514/* Print the description of exception KEY, ARGS to PORT, according to the
515   setting of "set guile print-stack".
516   If PORT is #f, use the standard error port.
517   If STACK is #f, never print the stack, regardless of whether printing it
518   is enabled.  If STACK is #t, then print it if it is contained in ARGS
519   (i.e., KEY is gdb:with-stack).  Otherwise STACK is the result of calling
520   scm_make_stack (which will be ignored in favor of the stack in ARGS if
521   KEY is gdb:with-stack).
522   KEY, ARGS are the standard arguments to scm_throw, et.al.
523
524   Basically this function is just a wrapper around calling
525   %print-exception-with-stack.  */
526
527void
528gdbscm_print_exception_with_stack (SCM port, SCM stack, SCM key, SCM args)
529{
530  SCM printer, status;
531
532  if (gdbscm_is_false (port))
533    port = scm_current_error_port ();
534
535  if (gdbscm_is_false (percent_print_exception_with_stack_var))
536    {
537      percent_print_exception_with_stack_var
538	= scm_c_private_variable (gdbscm_init_module_name,
539				  percent_print_exception_with_stack_name);
540      /* If we can't find %print-exception-with-stack, there's a problem on the
541	 Scheme side.  Don't kill GDB, just flag an error and leave it at
542	 that.  */
543      if (gdbscm_is_false (percent_print_exception_with_stack_var))
544	{
545	  gdbscm_printf (port, _("Error in Scheme exception printing,"
546				 " can't find %s.\n"),
547			 percent_print_exception_with_stack_name);
548	  return;
549	}
550    }
551  printer = scm_variable_ref (percent_print_exception_with_stack_var);
552
553  status = gdbscm_safe_call_4 (printer, port, stack, key, args, NULL);
554
555  /* If that failed still tell the user something.
556     But don't use the exception printing machinery!  */
557  if (gdbscm_is_exception (status))
558    {
559      gdbscm_printf (port, _("Error in Scheme exception printing:\n"));
560      scm_display (status, port);
561      scm_newline (port);
562    }
563}
564
565/* Print EXCEPTION, a <gdb:exception> object, to PORT.
566   If PORT is #f, use the standard error port.  */
567
568void
569gdbscm_print_gdb_exception (SCM port, SCM exception)
570{
571  gdb_assert (gdbscm_is_exception (exception));
572
573  gdbscm_print_exception_with_stack (port, SCM_BOOL_T,
574				     gdbscm_exception_key (exception),
575				     gdbscm_exception_args (exception));
576}
577
578/* Return a string description of <gdb:exception> EXCEPTION.
579   If EXCEPTION is a gdb:with-stack exception, unwrap it, a backtrace
580   is never returned as part of the result.  */
581
582gdb::unique_xmalloc_ptr<char>
583gdbscm_exception_message_to_string (SCM exception)
584{
585  SCM port = scm_open_output_string ();
586  SCM key, args;
587
588  gdb_assert (gdbscm_is_exception (exception));
589
590  key = gdbscm_exception_key (exception);
591  args = gdbscm_exception_args (exception);
592
593  if (scm_is_eq (key, with_stack_error_symbol)
594      /* Don't crash on a badly generated gdb:with-stack exception.  */
595      && scm_is_pair (args)
596      && scm_is_pair (scm_cdr (args)))
597    {
598      key = scm_car (args);
599      args = scm_cddr (args);
600    }
601
602  gdbscm_print_exception_message (port, SCM_BOOL_F, key, args);
603  gdb::unique_xmalloc_ptr<char> result
604    = gdbscm_scm_to_c_string (scm_get_output_string (port));
605  scm_close_port (port);
606  return result;
607}
608
609/* Return the value of the "guile print-stack" option as one of:
610   'none, 'message, 'full.  */
611
612static SCM
613gdbscm_percent_exception_print_style (void)
614{
615  if (gdbscm_print_excp == gdbscm_print_excp_none)
616    return none_symbol;
617  if (gdbscm_print_excp == gdbscm_print_excp_message)
618    return message_symbol;
619  if (gdbscm_print_excp == gdbscm_print_excp_full)
620    return full_symbol;
621  gdb_assert_not_reached ("bad value for \"guile print-stack\"");
622}
623
624/* Return the current <gdb:exception> counter.
625   This is for debugging purposes.  */
626
627static SCM
628gdbscm_percent_exception_count (void)
629{
630  return scm_from_ulong (gdbscm_exception_count);
631}
632
633/* Initialize the Scheme exception support.  */
634
635static const scheme_function exception_functions[] =
636{
637  { "make-exception", 2, 0, 0, as_a_scm_t_subr (gdbscm_make_exception),
638    "\
639Create a <gdb:exception> object.\n\
640\n\
641  Arguments: key args\n\
642    These are the standard key,args arguments of \"throw\"." },
643
644  { "exception?", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_p),
645    "\
646Return #t if the object is a <gdb:exception> object." },
647
648  { "exception-key", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_key),
649    "\
650Return the exception's key." },
651
652  { "exception-args", 1, 0, 0, as_a_scm_t_subr (gdbscm_exception_args),
653    "\
654Return the exception's arg list." },
655
656  END_FUNCTIONS
657};
658
659static const scheme_function private_exception_functions[] =
660{
661  { "%exception-print-style", 0, 0, 0,
662    as_a_scm_t_subr (gdbscm_percent_exception_print_style),
663    "\
664Return the value of the \"guile print-stack\" option." },
665
666  { "%exception-count", 0, 0, 0,
667    as_a_scm_t_subr (gdbscm_percent_exception_count),
668    "\
669Return a count of the number of <gdb:exception> objects created.\n\
670This is for debugging purposes." },
671
672  END_FUNCTIONS
673};
674
675void
676gdbscm_initialize_exceptions (void)
677{
678  exception_smob_tag = gdbscm_make_smob_type (exception_smob_name,
679					      sizeof (exception_smob));
680  scm_set_smob_print (exception_smob_tag, exscm_print_exception_smob);
681
682  gdbscm_define_functions (exception_functions, 1);
683  gdbscm_define_functions (private_exception_functions, 0);
684
685  error_symbol = scm_from_latin1_symbol ("gdb:error");
686
687  memory_error_symbol = scm_from_latin1_symbol ("gdb:memory-error");
688
689  user_error_symbol = scm_from_latin1_symbol ("gdb:user-error");
690
691  gdbscm_invalid_object_error_symbol
692    = scm_from_latin1_symbol ("gdb:invalid-object-error");
693
694  with_stack_error_symbol = scm_from_latin1_symbol ("gdb:with-stack");
695
696  /* The text of this symbol is taken from Guile's top-repl.scm.  */
697  signal_symbol = scm_from_latin1_symbol ("signal");
698
699  none_symbol = scm_from_latin1_symbol ("none");
700  message_symbol = scm_from_latin1_symbol ("message");
701  full_symbol = scm_from_latin1_symbol ("full");
702}
703