2 Copyright (C) 2001-2009, Parrot Foundation.
7 src/exceptions.c - Exceptions
11 Define the the core subsystem for exceptions.
13 =head2 Exception Functions
21 #include "parrot/parrot.h"
22 #include "parrot/call.h"
23 #include "parrot/exceptions.h"
24 #include "exceptions.str"
25 #include "pmc/pmc_continuation.h"
26 #include "pmc/pmc_context.h"
28 /* HEADERIZER HFILE: include/parrot/exceptions.h */
30 /* HEADERIZER BEGIN: static */
31 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
33 PARROT_CANNOT_RETURN_NULL
34 static PMC
* build_exception_from_args(PARROT_INTERP
,
36 ARGIN(const char *format
),
38 __attribute__nonnull__(1)
39 __attribute__nonnull__(3);
41 PARROT_CAN_RETURN_NULL
42 static void setup_exception_args(PARROT_INTERP
, ARGIN(const char *sig
), ...)
43 __attribute__nonnull__(1)
44 __attribute__nonnull__(2);
46 #define ASSERT_ARGS_build_exception_from_args __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
47 PARROT_ASSERT_ARG(interp) \
48 , PARROT_ASSERT_ARG(format))
49 #define ASSERT_ARGS_setup_exception_args __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
50 PARROT_ASSERT_ARG(interp) \
51 , PARROT_ASSERT_ARG(sig))
52 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
53 /* HEADERIZER END: static */
59 =item C<PMC * Parrot_ex_build_exception(PARROT_INTERP, INTVAL severity, long
62 Constructs a new exception object from the passed in arguments.
68 PARROT_CAN_RETURN_NULL
70 Parrot_ex_build_exception(PARROT_INTERP
, INTVAL severity
,
71 long error
, ARGIN_NULLOK(STRING
*msg
))
73 ASSERT_ARGS(Parrot_ex_build_exception
)
74 PMC
*exception
= pmc_new(interp
, enum_class_Exception
);
76 VTABLE_set_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "severity"), severity
);
77 VTABLE_set_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "type"), error
);
79 VTABLE_set_string_native(interp
, exception
, msg
);
86 =item C<void die_from_exception(PARROT_INTERP, PMC *exception)>
88 Print a stack trace for C<exception>, a message if there is one, and then exit.
94 PARROT_DOES_NOT_RETURN
96 die_from_exception(PARROT_INTERP
, ARGIN(PMC
*exception
))
98 ASSERT_ARGS(die_from_exception
)
99 STRING
* const message
= VTABLE_get_string(interp
, exception
);
100 INTVAL exit_status
= 1;
101 const INTVAL severity
= VTABLE_get_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "severity"));
103 /* In some cases we have a fatal exception before the IO system
104 * is completely initialized. Do some attempt to output the
105 * message to stderr, to help diagnosing. */
106 int use_perr
= !PMC_IS_NULL(Parrot_io_STDERR(interp
));
108 /* flush interpreter output to get things printed in order */
109 if (!PMC_IS_NULL(Parrot_io_STDOUT(interp
)))
110 Parrot_io_flush(interp
, Parrot_io_STDOUT(interp
));
112 Parrot_io_flush(interp
, Parrot_io_STDERR(interp
));
115 Interp
* interpdeb
= interp
->pdb
->debugger
;
117 Parrot_io_flush(interpdeb
, Parrot_io_STDOUT(interpdeb
));
118 Parrot_io_flush(interpdeb
, Parrot_io_STDERR(interpdeb
));
122 if (Parrot_str_not_equal(interp
, message
, CONST_STRING(interp
, ""))) {
124 Parrot_io_eprintf(interp
, "%S\n", message
);
126 char * const msg
= Parrot_str_to_cstring(interp
, message
);
128 fprintf(stderr
, "\n%s\n", msg
);
129 Parrot_str_free_cstring(msg
);
132 /* caution against output swap (with PDB_backtrace) */
134 PDB_backtrace(interp
);
136 else if (severity
== EXCEPT_exit
) {
137 /* TODO: get exit status based on type */
138 exit_status
= VTABLE_get_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "exit_code"));
141 Parrot_io_eprintf(interp
, "No exception handler and no message\n");
142 /* caution against output swap (with PDB_backtrace) */
144 PDB_backtrace(interp
);
148 * returning NULL from here returns resume address NULL to the
149 * runloop, which will terminate the thread function finally
151 * RT #45917 this check should better be in Parrot_exit
154 /* no exception handler, but this is not the main thread */
155 if (interp
->thread_data
&& interp
->thread_data
->tid
)
156 pt_thread_detach(interp
->thread_data
->tid
);
159 * only main should run the destroy functions - exit handler chain
160 * is freed during Parrot_exit
162 Parrot_exit(interp
, exit_status
);
167 =item C<void Parrot_ex_add_c_handler(PARROT_INTERP, Parrot_runloop *jp)>
169 Adds a new exception handler (defined in C) to the concurrency scheduler. Since
170 the exception handler is C code, it stores a runloop jump point to the start of
179 Parrot_ex_add_c_handler(PARROT_INTERP
, ARGIN(Parrot_runloop
*jp
))
181 ASSERT_ARGS(Parrot_ex_add_c_handler
)
182 PMC
* const handler
= pmc_new(interp
, enum_class_ExceptionHandler
);
183 /* Flag to mark a C exception handler */
184 PObj_get_FLAGS(handler
) |= SUB_FLAG_C_HANDLER
;
185 VTABLE_set_pointer(interp
, handler
, jp
);
186 Parrot_cx_add_handler_local(interp
, handler
);
191 =item C<opcode_t * Parrot_ex_throw_from_op(PARROT_INTERP, PMC *exception, void
194 Throw an exception from inside an op. Looks for an exception handler in the
195 current concurrency scheduler. If a suitable handler is found, invoke it and
196 return the address where execution should continue. If no handler is found,
197 the exception message is printed along with the current line number
198 annotation and a backtrace before exiting Parrot.
205 PARROT_CAN_RETURN_NULL
207 Parrot_ex_throw_from_op(PARROT_INTERP
, ARGIN(PMC
*exception
), ARGIN_NULLOK(void *dest
))
209 ASSERT_ARGS(Parrot_ex_throw_from_op
)
211 PMC
* const handler
= Parrot_cx_find_handler_local(interp
, exception
);
212 if (PMC_IS_NULL(handler
)) {
213 STRING
* const message
= VTABLE_get_string(interp
, exception
);
214 const INTVAL severity
= VTABLE_get_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "severity"));
215 if (severity
< EXCEPT_error
) {
216 PMC
* const resume
= VTABLE_get_attr_str(interp
, exception
, CONST_STRING(interp
, "resume"));
217 if (Parrot_str_not_equal(interp
, message
, CONST_STRING(interp
, ""))) {
218 Parrot_io_eprintf(interp
, "%S\n", message
);
221 Parrot_io_eprintf(interp
, "%S\n", CONST_STRING(interp
, "Warning"));
224 /* caution against output swap (with PDB_backtrace) */
226 /* PDB_backtrace(interp); */
228 if (!PMC_IS_NULL(resume
)) {
229 return VTABLE_invoke(interp
, resume
, NULL
);
232 die_from_exception(interp
, exception
);
235 address
= VTABLE_invoke(interp
, handler
, dest
);
236 setup_exception_args(interp
, "P", exception
);
238 if (PObj_get_FLAGS(handler
) & SUB_FLAG_C_HANDLER
) {
239 /* it's a C exception handler */
240 Parrot_runloop
* const jump_point
= (Parrot_runloop
*)address
;
241 longjmp(jump_point
->resume
, 1);
244 /* return the address of the handler */
250 =item C<static void setup_exception_args(PARROT_INTERP, const char *sig, ...)>
252 Sets up arguments to the exception handler invocation.
258 PARROT_CAN_RETURN_NULL
260 setup_exception_args(PARROT_INTERP
, ARGIN(const char *sig
), ...)
262 ASSERT_ARGS(setup_exception_args
)
267 sig_obj
= Parrot_pcc_build_sig_object_from_varargs(interp
, PMCNULL
, sig
, args
);
270 CALLSIGNATURE_is_exception_SET(sig_obj
);
272 Parrot_pcc_set_signature(interp
, CURRENT_CONTEXT(interp
), sig_obj
);
277 =item C<static PMC * build_exception_from_args(PARROT_INTERP, int ex_type, const
278 char *format, va_list arglist)>
280 Builds an exception PMC with the given integer type C<ex_type>, and the string
281 message given as a format/arglist combo, like is used by the Parrot_vsprintf*
288 PARROT_CANNOT_RETURN_NULL
290 build_exception_from_args(PARROT_INTERP
, int ex_type
,
291 ARGIN(const char *format
), va_list arglist
)
293 ASSERT_ARGS(build_exception_from_args
)
294 /* Make and set exception message. */
297 ? Parrot_vsprintf_c(interp
, format
, arglist
)
298 : string_make(interp
, format
, strlen(format
), NULL
, 0);
300 return Parrot_ex_build_exception(interp
, EXCEPT_error
, ex_type
, msg
);
305 =item C<void Parrot_ex_throw_from_c(PARROT_INTERP, PMC *exception)>
307 Throws an exception object from any location in C code. A suitable handler
308 is retrieved from the concurrency scheduler. If the handler is found, control
309 flow is passed to it. Handlers can be either C-level or PIR-level routines. If
310 no suitable handler is found, Parrot exits with the stored exception error
313 See also C<exit_fatal()>, which signals fatal errors, and
314 C<Parrot_ex_throw_from_op> which throws an exception from within an op.
316 The 'invoke' vtable function doesn't actually execute a
317 sub/continuation/handler, it only sets up the environment for invocation and
318 returns the address of the start of the sub's code. That address then becomes
319 the next op in the runloop.
321 Exceptions thrown from C and caught by a continuation-based handler are
322 resumable at the level of a C instruction. When handled, they return the
323 exception object. Any values returned from the handler to the C code that threw
324 the exception can be stored in the exception's payload.
331 PARROT_DOES_NOT_RETURN
333 Parrot_ex_throw_from_c(PARROT_INTERP
, ARGIN(PMC
*exception
))
335 ASSERT_ARGS(Parrot_ex_throw_from_c
)
337 Parrot_runloop
*return_point
= interp
->current_runloop
;
339 PMC
* const handler
=
340 Parrot_cx_find_handler_local(interp
, exception
);
342 if (PMC_IS_NULL(handler
))
343 die_from_exception(interp
, exception
);
345 if (Interp_debug_TEST(interp
, PARROT_BACKTRACE_DEBUG_FLAG
)) {
346 STRING
* const exit_code
= CONST_STRING(interp
, "exit_code");
347 STRING
* const msg
= VTABLE_get_string(interp
, exception
);
348 int exitcode
= VTABLE_get_integer_keyed_str(interp
,
349 exception
, exit_code
);
351 Parrot_io_eprintf(interp
,
352 "Parrot_ex_throw_from_c (severity:%d error:%d): %Ss\n",
353 EXCEPT_error
, exitcode
, msg
);
354 PDB_backtrace(interp
);
358 * XXX TT#596 - pass in current context instead when we have context PMCs. */
359 /* Don't split line. It will break CONST_STRING handling */
360 VTABLE_set_attr_str(interp
, exception
, CONST_STRING(interp
, "thrower"), Parrot_pcc_get_continuation(interp
, CURRENT_CONTEXT(interp
)));
363 /* it's a C exception handler */
364 if (PObj_get_FLAGS(handler
) & SUB_FLAG_C_HANDLER
) {
365 Parrot_runloop
* const jump_point
=
366 (Parrot_runloop
* const)VTABLE_get_pointer(interp
, handler
);
367 longjmp(jump_point
->resume
, 1);
370 /* Run the handler. */
371 address
= VTABLE_invoke(interp
, handler
, NULL
);
372 setup_exception_args(interp
, "P", exception
);
373 PARROT_ASSERT(return_point
->handler_start
== NULL
);
374 return_point
->handler_start
= address
;
375 longjmp(return_point
->resume
, 2);
380 =item C<opcode_t * Parrot_ex_throw_from_op_args(PARROT_INTERP, void *dest, int
381 ex_type, const char *format, ...)>
383 Throws an exception from an opcode, with an error message constructed
384 from a format string and arguments. Constructs an Exception PMC, and passes it
385 to C<Parrot_ex_throw_from_op>.
387 See also C<Parrot_ex_throw_from_c> and C<exit_fatal()>.
394 PARROT_CAN_RETURN_NULL
396 Parrot_ex_throw_from_op_args(PARROT_INTERP
, ARGIN_NULLOK(void *dest
),
397 int ex_type
, ARGIN(const char *format
), ...)
399 ASSERT_ARGS(Parrot_ex_throw_from_op_args
)
403 va_start(arglist
, format
);
404 exception
= build_exception_from_args(interp
, ex_type
, format
, arglist
);
407 return Parrot_ex_throw_from_op(interp
, exception
, dest
);
412 =item C<void Parrot_ex_throw_from_c_args(PARROT_INTERP, void *ret_addr, int
413 exitcode, const char *format, ...)>
415 Throws an exception, with an error message constructed from a format string and
416 arguments. C<ret_addr> is the address from which to resume, if some handler
417 decides that is appropriate, or zero to make the error non-resumable.
418 C<exitcode> is a C<exception_type_enum> value. Constructs an Exception PMC
419 and passes it to C<Parrot_ex_throw_from_c>.
421 See also C<Parrot_ex_throw_from_op> and C<exit_fatal()>.
428 PARROT_DOES_NOT_RETURN
430 Parrot_ex_throw_from_c_args(PARROT_INTERP
, SHIM(void *ret_addr
),
431 int exitcode
, ARGIN(const char *format
), ...)
433 ASSERT_ARGS(Parrot_ex_throw_from_c_args
)
437 va_start(arglist
, format
);
438 exception
= build_exception_from_args(interp
, exitcode
, format
, arglist
);
441 Parrot_ex_throw_from_c(interp
, exception
);
446 =item C<opcode_t * Parrot_ex_rethrow_from_op(PARROT_INTERP, PMC *exception)>
448 Rethrow the given exception from an op, if it has previously been thrown and
449 not handled by the provided handler. Marks the exception object as being
450 unhandled and throws it again.
457 PARROT_WARN_UNUSED_RESULT
458 PARROT_CAN_RETURN_NULL
460 Parrot_ex_rethrow_from_op(PARROT_INTERP
, ARGIN(PMC
*exception
))
462 ASSERT_ARGS(Parrot_ex_rethrow_from_op
)
464 Parrot_ex_mark_unhandled(interp
, exception
);
466 return Parrot_ex_throw_from_op(interp
, exception
, NULL
);
471 =item C<void Parrot_ex_rethrow_from_c(PARROT_INTERP, PMC *exception)>
473 Rethrow the exception from C code. Marks the Exception PMC as being unhandled
481 PARROT_DOES_NOT_RETURN
483 Parrot_ex_rethrow_from_c(PARROT_INTERP
, ARGIN(PMC
*exception
))
485 ASSERT_ARGS(Parrot_ex_rethrow_from_c
)
486 Parrot_ex_mark_unhandled(interp
, exception
);
488 Parrot_ex_throw_from_c(interp
, exception
);
493 =item C<void Parrot_ex_mark_unhandled(PARROT_INTERP, PMC *exception)>
495 Mark an exception as unhandled, as part of rethrowing it.
503 Parrot_ex_mark_unhandled(PARROT_INTERP
, ARGIN(PMC
*exception
))
505 ASSERT_ARGS(Parrot_ex_mark_unhandled
)
506 VTABLE_set_integer_keyed_str(interp
, exception
, CONST_STRING(interp
, "handled"), -1);
511 =item C<size_t Parrot_ex_calc_handler_offset(PARROT_INTERP)>
513 Retrieve an exception from the concurrency scheduler, prepare a call to the
514 handler, and return the offset to the handler so it can become the next op in
517 TT #546: This function appears to be unused.
525 Parrot_ex_calc_handler_offset(PARROT_INTERP
)
527 ASSERT_ARGS(Parrot_ex_calc_handler_offset
)
528 PMC
* const exception
= VTABLE_pop_pmc(interp
, interp
->scheduler
);
530 /* now fill rest of exception, locate handler and get
531 * destination of handler */
532 opcode_t
* const handler_address
= Parrot_ex_throw_from_op(interp
, exception
, NULL
);
534 if (handler_address
== NULL
)
535 PANIC(interp
, "Unable to calculate opcode address for exception handler");
537 /* return the *offset* of the handler */
538 return handler_address
- interp
->code
->base
.data
;
545 =head2 Error Functions
549 =item C<PARROT_DOES_NOT_RETURN_WHEN_FALSE void Parrot_assert(INTVAL condition,
550 const char *condition_string, const char *file, unsigned int line)>
552 A better version of assert() that gives a backtrace.
559 PARROT_DOES_NOT_RETURN_WHEN_FALSE
561 Parrot_assert(INTVAL condition
, ARGIN(const char *condition_string
),
562 ARGIN(const char *file
), unsigned int line
)
564 ASSERT_ARGS(Parrot_assert
)
566 Parrot_confess(condition_string
, file
, line
);
571 =item C<void Parrot_confess(const char *cond, const char *file, unsigned int
574 Prints a backtrace and message for a failed assertion.
581 PARROT_DOES_NOT_RETURN
583 Parrot_confess(ARGIN(const char *cond
), ARGIN(const char *file
), unsigned int line
)
585 ASSERT_ARGS(Parrot_confess
)
586 fprintf(stderr
, "%s:%u: failed assertion '%s'\n", file
, line
, cond
);
587 Parrot_print_backtrace();
593 =item C<void Parrot_print_backtrace(void)>
595 Displays the primrose path to disaster, (the stack frames leading up to the
596 abort). Used by C<Parrot_confess>.
603 Parrot_print_backtrace(void)
605 ASSERT_ARGS(Parrot_print_backtrace
)
606 #ifdef PARROT_HAS_BACKTRACE
607 # define BACKTRACE_DEPTH 32
608 /*# define BACKTRACE_VERBOSE */
609 # ifndef PARROT_HAS_DLINFO
610 # define BACKTRACE_VERBOSE
612 /* stolen from http://www.delorie.com/gnu/docs/glibc/libc_665.html */
613 void *array
[BACKTRACE_DEPTH
];
616 const int size
= backtrace(array
, BACKTRACE_DEPTH
);
619 "Backtrace - Obtained %d stack frames (max trace depth is %d).\n",
620 size
, BACKTRACE_DEPTH
);
621 # ifndef BACKTRACE_VERBOSE
622 for (i
= 0; i
< size
; i
++) {
624 const int found
= dladdr(array
[i
], &frameInfo
);
627 const int indent
= 2 + (2 * i
);
629 fprintf(stderr
, "%*s", indent
, "");
631 if (found
&& frameInfo
.dli_sname
)
632 fprintf(stderr
, "%s\n", frameInfo
.dli_sname
);
634 fprintf(stderr
, "(unknown)\n");
638 { /* Scope for strings */
639 char ** strings
= backtrace_symbols(array
, size
);
641 for (i
= 0; i
< size
; i
++)
642 fprintf(stderr
, "%s\n", strings
[i
]);
643 /* backtrace_symbols gets memory using malloc */
647 fputs("Not enough memory for backtrace_symbols\n", stderr
);
651 # undef BACKTRACE_DEPTH
652 #endif /* ifdef PARROT_HAS_BACKTRACE */
657 =item C<void exit_fatal(int exitcode, const char *format, ...)>
659 Signal a fatal error condition. This should only be used with dire errors that
660 cannot throw an exception (because no interpreter is available, or the nature
661 of the error would interfere with the exception system).
663 This involves printing an error message to stderr, and calling C<exit> to exit
664 the process with the given exitcode. It is not possible for Parrot bytecode to
665 intercept a fatal error (for that, use C<Parrot_ex_throw_from_c_args>).
666 C<exit_fatal> does not call C<Parrot_exit> to invoke exit handlers (that would
667 require an interpreter).
674 PARROT_DOES_NOT_RETURN
676 exit_fatal(int exitcode
, ARGIN(const char *format
), ...)
678 ASSERT_ARGS(exit_fatal
)
680 va_start(arglist
, format
);
681 vfprintf(stderr
, format
, arglist
);
682 fprintf(stderr
, "\n");
683 /* caution against output swap (with PDB_backtrace) */
689 /* The DUMPCORE macro is defined for most platforms, but defined here if not
690 * found elsewhere, so we're sure it's safe to call. */
693 # define DUMPCORE() \
694 fprintf(stderr, "Sorry, coredump is not yet implemented " \
695 "for this platform.\n\n"); \
701 =item C<void do_panic(NULLOK_INTERP, const char *message, const char *file,
704 Panic handler. Things have gone very wrong in an unexpected way. Print out an
705 error message and instructions for the user to report the error to the
712 PARROT_DOES_NOT_RETURN
714 do_panic(NULLOK_INTERP
, ARGIN_NULLOK(const char *message
),
715 ARGIN_NULLOK(const char *file
), unsigned int line
)
717 ASSERT_ARGS(do_panic
)
718 /* Note: we can't format any floats in here--Parrot_sprintf
719 ** may panic because of floats.
720 ** and we don't use Parrot_sprintf or such, because we are
721 ** already in panic --leo
723 fprintf(stderr
, "Parrot VM: PANIC: %s!\n",
724 message
? message
: "(no message available)");
726 fprintf(stderr
, "C file %s, line %u\n",
727 file
? file
: "(not available)", line
);
729 fprintf(stderr
, "Parrot file (not available), ");
730 fprintf(stderr
, "line (not available)\n");
733 We highly suggest you notify the Parrot team if you have not been working on\n\
734 Parrot. Use parrotbug (located in parrot's root directory) or send an\n\
735 e-mail to parrot-dev@lists.parrot.org.\n\
736 Include the entire text of this error message and the text of the script that\n\
737 generated the error. If you've made any modifications to Parrot, please\n\
738 describe them as well.\n\n");
740 fprintf(stderr
, "Version : %s\n", PARROT_VERSION
);
741 fprintf(stderr
, "Configured : %s\n", PARROT_CONFIG_DATE
);
742 fprintf(stderr
, "Architecture: %s\n", PARROT_ARCHNAME
);
743 fprintf(stderr
, "JIT Capable : %s\n", JIT_CAPABLE
? "Yes" : "No");
745 fprintf(stderr
, "Interp Flags: %#x\n", (unsigned int)interp
->flags
);
747 fprintf(stderr
, "Interp Flags: (no interpreter)\n");
748 fprintf(stderr
, "Exceptions : %s\n", "(missing from core)");
749 fprintf(stderr
, "\nDumping Core...\n");
761 F<include/parrot/exceptions.h>.
770 * c-file-style: "parrot"
772 * vim: expandtab shiftwidth=4: