1 /*
2 Copyright (C) 2001-2014, Parrot Foundation.
3 
4 =head1 NAME
5 
6 src/exceptions.c - Exceptions
7 
8 =head1 DESCRIPTION
9 
10 Define the core subsystem for exceptions.
11 
12 =head2 Exception Functions
13 
14 =over 4
15 
16 =cut
17 
18 */
19 
20 #include "parrot/parrot.h"
21 #include "exceptions.str"
22 #include "pmc/pmc_continuation.h"
23 #include "pmc/pmc_exception.h"
24 #include "parrot/exceptions.h"
25 #include "parrot/events.h"
26 
27 /* HEADERIZER HFILE: include/parrot/exceptions.h */
28 
29 /* HEADERIZER BEGIN: static */
30 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
31 
32 PARROT_WARN_UNUSED_RESULT
33 PARROT_CANNOT_RETURN_NULL
34 static PMC * build_exception(PARROT_INTERP,
35     int ex_type,
36     ARGIN(const char *msg))
37         __attribute__nonnull__(1)
38         __attribute__nonnull__(3);
39 
40 PARROT_WARN_UNUSED_RESULT
41 PARROT_CANNOT_RETURN_NULL
42 static PMC * build_exception_from_args(PARROT_INTERP,
43     int ex_type,
44     ARGIN(const char *format),
45     va_list arglist)
46         __attribute__nonnull__(1)
47         __attribute__nonnull__(3);
48 
49 static void Parrot_ex_update_for_rethrow(PARROT_INTERP, ARGMOD(PMC * ex))
50         __attribute__nonnull__(1)
51         __attribute__nonnull__(2)
52         FUNC_MODIFIES(* ex);
53 
54 PARROT_CAN_RETURN_NULL
55 static void setup_exception_args(PARROT_INTERP, ARGIN(const char *sig), ...)
56         __attribute__nonnull__(1)
57         __attribute__nonnull__(2);
58 
59 #define ASSERT_ARGS_build_exception __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
60        PARROT_ASSERT_ARG(interp) \
61     , PARROT_ASSERT_ARG(msg))
62 #define ASSERT_ARGS_build_exception_from_args __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
63        PARROT_ASSERT_ARG(interp) \
64     , PARROT_ASSERT_ARG(format))
65 #define ASSERT_ARGS_Parrot_ex_update_for_rethrow __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
66        PARROT_ASSERT_ARG(interp) \
67     , PARROT_ASSERT_ARG(ex))
68 #define ASSERT_ARGS_setup_exception_args __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
69        PARROT_ASSERT_ARG(interp) \
70     , PARROT_ASSERT_ARG(sig))
71 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
72 /* HEADERIZER END: static */
73 
74 #include <stdarg.h>
75 
76 /*
77 
78 =item C<PMC * Parrot_ex_build_exception(PARROT_INTERP, INTVAL severity, long
79 error, STRING *msg)>
80 
81 Constructs a new exception object from the passed in arguments.
82 
83 =cut
84 
85 */
86 PARROT_EXPORT
87 PARROT_CANNOT_RETURN_NULL
88 PMC *
Parrot_ex_build_exception(PARROT_INTERP,INTVAL severity,long error,ARGIN_NULLOK (STRING * msg))89 Parrot_ex_build_exception(PARROT_INTERP, INTVAL severity,
90         long error, ARGIN_NULLOK(STRING *msg))
91 {
92     ASSERT_ARGS(Parrot_ex_build_exception)
93     const int exception_type_id = Parrot_hll_get_ctx_HLL_type(interp, enum_class_Exception);
94     PMC * const exception = Parrot_pmc_new(interp, exception_type_id);
95 
96     VTABLE_set_integer_keyed_str(interp, exception, CONST_STRING(interp, "severity"), severity);
97     VTABLE_set_integer_keyed_str(interp, exception, CONST_STRING(interp, "type"), error);
98     if (msg)
99         VTABLE_set_string_native(interp, exception, msg);
100 
101     return exception;
102 }
103 
104 /*
105 
106 =item C<void die_from_exception(PARROT_INTERP, PMC *exception)>
107 
108 Print a stack trace for C<exception>, a message if there is one, and then exit.
109 
110 =cut
111 
112 */
113 
114 PARROT_DOES_NOT_RETURN
115 PARROT_COLD
116 void
die_from_exception(PARROT_INTERP,ARGIN (PMC * exception))117 die_from_exception(PARROT_INTERP, ARGIN(PMC *exception))
118 {
119     ASSERT_ARGS(die_from_exception)
120     /* Avoid anything that can throw if we are already throwing from
121      * a previous call to this function */
122     int already_dying = !PMC_IS_NULL(interp->final_exception);
123     STRING * const message     = already_dying ? STRINGNULL :
124             VTABLE_get_string(interp, exception);
125     const INTVAL   severity    = already_dying ? (INTVAL)EXCEPT_fatal :
126             VTABLE_get_integer_keyed_str(interp, exception, CONST_STRING(interp, "severity"));
127 
128     if (already_dying) {
129         Parrot_x_jump_out(interp, 1);
130     }
131     else {
132         /* In some cases we have a fatal exception before the IO system
133          * is completely initialized. Do some attempt to output the
134          * message to stderr, to help diagnosing. */
135         PMC * const pout =  Parrot_io_STDOUT(interp);
136         PMC * const perr =  Parrot_io_STDERR(interp);
137         interp->final_exception = exception;
138         interp->exit_code = 1;
139 
140         /* flush interpreter output to get things printed in order */
141         if (!PMC_IS_NULL(pout))
142             Parrot_io_flush(interp, pout);
143         if (!PMC_IS_NULL(perr))
144             Parrot_io_flush(interp, perr);
145 
146         if (interp->pdb) {
147             Interp * const interpdeb = interp->pdb->debugger;
148             if (interpdeb) {
149                 Parrot_io_flush(interpdeb, Parrot_io_STDOUT(interpdeb));
150                 Parrot_io_flush(interpdeb, Parrot_io_STDERR(interpdeb));
151             }
152         }
153 
154 
155         if (severity == EXCEPT_exit) {
156             /* TODO: get exit status based on type */
157             interp->exit_code = VTABLE_get_integer_keyed_str(interp, exception, CONST_STRING(interp, "exit_code"));
158         }
159         else if (!STRING_length(message)) {
160             STRING * const newmessage = CONST_STRING(interp, "No exception handler and no message\n");
161             VTABLE_set_string_native(interp, exception, newmessage);
162         }
163     }
164 
165     Parrot_x_jump_out(interp, interp->exit_code);
166 }
167 
168 /*
169 
170 =item C<void Parrot_ex_add_c_handler(PARROT_INTERP, Parrot_runloop *jp)>
171 
172 Adds a new exception handler (defined in C) to the concurrency scheduler. Since
173 the exception handler is C code, it stores a runloop jump point to the start of
174 the handler code.
175 
176 =cut
177 
178 */
179 
180 PARROT_EXPORT
181 void
Parrot_ex_add_c_handler(PARROT_INTERP,ARGIN (Parrot_runloop * jp))182 Parrot_ex_add_c_handler(PARROT_INTERP, ARGIN(Parrot_runloop *jp))
183 {
184     ASSERT_ARGS(Parrot_ex_add_c_handler)
185     PMC * const handler = Parrot_pmc_new(interp, enum_class_ExceptionHandler);
186     /* Flag to mark a C exception handler */
187     PObj_get_FLAGS(handler) |= SUB_FLAG_C_HANDLER;
188     VTABLE_set_pointer(interp, handler, jp);
189     Parrot_cx_add_handler_local(interp, handler);
190 }
191 
192 /*
193 
194 =item C<opcode_t * Parrot_ex_throw_from_op(PARROT_INTERP, PMC *exception, void
195 *dest)>
196 
197 Throw an exception from inside an op. Looks for an exception handler in the
198 current concurrency scheduler. If a suitable handler is found, invoke it and
199 return the address where execution should continue. If no handler is found,
200 the exception message is printed along with the current line number
201 annotation and a backtrace before exiting Parrot.
202 
203 =cut
204 
205 */
206 
207 PARROT_EXPORT
208 PARROT_CAN_RETURN_NULL
209 opcode_t *
Parrot_ex_throw_from_op(PARROT_INTERP,ARGIN (PMC * exception),ARGIN_NULLOK (void * dest))210 Parrot_ex_throw_from_op(PARROT_INTERP, ARGIN(PMC *exception), ARGIN_NULLOK(void *dest))
211 {
212     ASSERT_ARGS(Parrot_ex_throw_from_op)
213     opcode_t   *address;
214     PMC        *handler;
215 
216     /* Note the thrower. */
217     VTABLE_set_attr_str(interp, exception, CONST_STRING(interp, "thrower"), CURRENT_CONTEXT(interp));
218 
219     /* Locate the handler, if there is one. */
220     handler = Parrot_cx_find_handler_local(interp, exception);
221     if (PMC_IS_NULL(handler)) {
222         STRING * const message     = VTABLE_get_string(interp, exception);
223         const INTVAL   severity    = VTABLE_get_integer_keyed_str(interp, exception, CONST_STRING(interp, "severity"));
224         if (severity < EXCEPT_error) {
225             PMC * const resume = VTABLE_get_attr_str(interp, exception, CONST_STRING(interp, "resume"));
226             if (STRING_length(message)) {
227                 Parrot_io_eprintf(interp, "%S\n", message);
228             }
229             else {
230                 Parrot_io_eprintf(interp, "%S\n", CONST_STRING(interp, "Warning"));
231             }
232 
233             /* caution against output swap (with PDB_backtrace) */
234             fflush(stderr);
235             /* PDB_backtrace(interp); */
236 
237             if (!PMC_IS_NULL(resume)) {
238                 return VTABLE_invoke(interp, resume, NULL);
239             }
240         }
241         die_from_exception(interp, exception);
242     }
243 
244     address    = VTABLE_invoke(interp, handler, dest);
245     setup_exception_args(interp, "P", exception);
246 
247     if (PObj_get_FLAGS(handler) & SUB_FLAG_C_HANDLER) {
248         /* it's a C exception handler */
249         Parrot_runloop * const jump_point = (Parrot_runloop *)address;
250         jump_point->exception = exception;
251         longjmp(jump_point->resume, PARROT_JMP_EXCEPTION_HANDLED);
252     }
253 
254     /* return the address of the handler */
255     return address;
256 }
257 
258 /*
259 
260 =item C<static void setup_exception_args(PARROT_INTERP, const char *sig, ...)>
261 
262 Sets up arguments to the exception handler invocation.
263 
264 =cut
265 
266 */
267 
268 PARROT_CAN_RETURN_NULL
269 static void
setup_exception_args(PARROT_INTERP,ARGIN (const char * sig),...)270 setup_exception_args(PARROT_INTERP, ARGIN(const char *sig), ...)
271 {
272     ASSERT_ARGS(setup_exception_args)
273     va_list  args;
274     PMC     *sig_obj;
275 
276     va_start(args, sig);
277     sig_obj = Parrot_pcc_build_call_from_varargs(interp, PMCNULL, sig, &args);
278     va_end(args);
279 
280     CALLSIGNATURE_is_exception_SET(sig_obj);
281 
282     Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), sig_obj);
283 }
284 
285 /*
286 
287 =item C<static PMC * build_exception_from_args(PARROT_INTERP, int ex_type, const
288 char *format, va_list arglist)>
289 
290 Builds an exception PMC with the given integer type C<ex_type>, and the string
291 message given as a format/arglist combo, like is used by the Parrot_vsprintf*
292 family of functions.
293 
294 =cut
295 
296 */
297 
298 PARROT_WARN_UNUSED_RESULT
299 PARROT_CANNOT_RETURN_NULL
300 static PMC *
build_exception_from_args(PARROT_INTERP,int ex_type,ARGIN (const char * format),va_list arglist)301 build_exception_from_args(PARROT_INTERP, int ex_type,
302         ARGIN(const char *format), va_list arglist)
303 {
304     ASSERT_ARGS(build_exception_from_args)
305     /* Make and set exception message. */
306     STRING * const msg =
307         strchr(format, '%')
308             ? Parrot_vsprintf_c(interp, format, arglist)
309             : Parrot_str_new_init(interp, format, strlen(format),
310                     Parrot_default_encoding_ptr, 0);
311 
312     return Parrot_ex_build_exception(interp, EXCEPT_error, ex_type, msg);
313 }
314 
315 /*
316 
317 =item C<static PMC * build_exception(PARROT_INTERP, int ex_type, const char
318 *msg)>
319 
320 Builds an exception PMC with the given integer C<ex_type>, and the constant
321 string message.
322 
323 =cut
324 
325 */
326 
327 PARROT_WARN_UNUSED_RESULT
328 PARROT_CANNOT_RETURN_NULL
329 static PMC *
build_exception(PARROT_INTERP,int ex_type,ARGIN (const char * msg))330 build_exception(PARROT_INTERP, int ex_type,
331         ARGIN(const char *msg))
332 {
333     ASSERT_ARGS(build_exception)
334     STRING * const str = Parrot_str_new_init(interp, msg, strlen(msg),
335                              Parrot_default_encoding_ptr, 0);
336     return Parrot_ex_build_exception(interp, EXCEPT_error, ex_type, str);
337 }
338 
339 /*
340 
341 =item C<void Parrot_ex_throw_from_c(PARROT_INTERP, PMC *exception)>
342 
343 Throws an exception object from any location in C code. A suitable handler
344 is retrieved from the concurrency scheduler. If the handler is found, control
345 flow is passed to it. Handlers can be either C-level or PIR-level routines. If
346 no suitable handler is found, Parrot exits with the stored exception error
347 message.
348 
349 See also C<src/exit.c> and
350 C<Parrot_ex_throw_from_op> which throws an exception from within an op.
351 
352 The 'invoke' vtable function doesn't actually execute a
353 sub/continuation/handler, it only sets up the environment for invocation and
354 returns the address of the start of the sub's code. That address then becomes
355 the next op in the runloop.
356 
357 Exceptions thrown from C and caught by a continuation-based handler are
358 resumable at the level of a C instruction. When handled, they return the
359 exception object. Any values returned from the handler to the C code that threw
360 the exception can be stored in the exception's payload.
361 
362 =cut
363 
364 */
365 
366 PARROT_EXPORT
367 PARROT_DOES_NOT_RETURN
368 PARROT_COLD
369 void
Parrot_ex_throw_from_c(PARROT_INTERP,ARGIN (PMC * exception))370 Parrot_ex_throw_from_c(PARROT_INTERP, ARGIN(PMC *exception))
371 {
372     ASSERT_ARGS(Parrot_ex_throw_from_c)
373 
374     PMC * const handler = Parrot_cx_find_handler_local(interp, exception);
375 
376     if (Interp_debug_TEST(interp, PARROT_BACKTRACE_DEBUG_FLAG)) {
377         STRING * const exit_code = CONST_STRING(interp, "exit_code");
378         STRING * const msg       = VTABLE_get_string(interp, exception);
379         const int exitcode       = VTABLE_get_integer_keyed_str(interp, exception, exit_code);
380 
381         Parrot_io_eprintf(interp,
382             "Parrot_ex_throw_from_c (severity:%d error:%d): %Ss\n",
383             EXCEPT_error, exitcode, msg);
384         PDB_backtrace(interp);
385     }
386 
387     /* Note the thrower.
388      * Don't split line. It will break CONST_STRING handling. */
389     VTABLE_set_attr_str(interp, exception, CONST_STRING(interp, "thrower"), CURRENT_CONTEXT(interp));
390 
391     if (PMC_IS_NULL(handler))
392         die_from_exception(interp, exception);
393 
394     /* it's a C exception handler */
395     if (PObj_get_FLAGS(handler) & SUB_FLAG_C_HANDLER) {
396         Parrot_runloop * const jump_point =
397             (Parrot_runloop *)VTABLE_get_pointer(interp, handler);
398         jump_point->exception = exception;
399         longjmp(jump_point->resume, PARROT_JMP_EXCEPTION_HANDLED);
400     }
401     else {
402         /* Run the handler. */
403         Parrot_runloop * const return_point = interp->current_runloop;
404         opcode_t              *address = VTABLE_invoke(interp, handler, NULL);
405         setup_exception_args(interp, "P", exception);
406         PARROT_ASSERT(return_point->handler_start == NULL);
407         return_point->handler_start = address;
408         longjmp(return_point->resume, PARROT_JMP_EXCEPTION_FROM_C);
409     }
410 }
411 
412 /*
413 
414 =item C<opcode_t * Parrot_ex_throw_from_op_args(PARROT_INTERP, void *dest, int
415 ex_type, const char *format, ...)>
416 
417 Throws an exception from an opcode, with an error message constructed
418 from a format string and arguments. Constructs an Exception PMC, and passes it
419 to C<Parrot_ex_throw_from_op>.
420 
421 See also C<Parrot_ex_throw_from_c> and C<src/exit.c>.
422 
423 =cut
424 
425 */
426 
427 PARROT_EXPORT
428 PARROT_CAN_RETURN_NULL
429 opcode_t *
Parrot_ex_throw_from_op_args(PARROT_INTERP,ARGIN_NULLOK (void * dest),int ex_type,ARGIN (const char * format),...)430 Parrot_ex_throw_from_op_args(PARROT_INTERP, ARGIN_NULLOK(void *dest),
431         int ex_type, ARGIN(const char *format), ...)
432 {
433     ASSERT_ARGS(Parrot_ex_throw_from_op_args)
434     PMC *exception;
435 
436     va_list arglist;
437     va_start(arglist, format);
438     exception = build_exception_from_args(interp, ex_type, format, arglist);
439     va_end(arglist);
440 
441     return Parrot_ex_throw_from_op(interp, exception, dest);
442 }
443 
444 /*
445 
446 =item C<void Parrot_ex_throw_from_c_noargs(PARROT_INTERP, int exitcode, const
447 char *msg)>
448 
449 Throws an exception, with a constant error message.
450 C<exitcode> is a C<exception_type_enum> value. Constructs an Exception PMC
451 and passes it to C<Parrot_ex_throw_from_c>.
452 
453 See also C<Parrot_ex_throw_from_c_args>.
454 
455 =cut
456 
457 */
458 
459 PARROT_EXPORT
460 PARROT_DOES_NOT_RETURN
461 PARROT_COLD
462 void
Parrot_ex_throw_from_c_noargs(PARROT_INTERP,int exitcode,ARGIN (const char * msg))463 Parrot_ex_throw_from_c_noargs(PARROT_INTERP, int exitcode, ARGIN(const char *msg))
464 {
465     ASSERT_ARGS(Parrot_ex_throw_from_c_noargs)
466     PMC *exception = build_exception(interp, exitcode, msg);
467     Parrot_ex_throw_from_c(interp, exception);
468 }
469 
470 /*
471 
472 =item C<void Parrot_ex_throw_from_c_args(PARROT_INTERP, void *ret_addr, int
473 exitcode, const char *format, ...)>
474 
475 Throws an exception, with an error message constructed from a format string and
476 arguments. C<ret_addr> is the address from which to resume, if some handler
477 decides that is appropriate, or zero to make the error non-resumable.
478 C<exitcode> is a C<exception_type_enum> value. Constructs an Exception PMC
479 and passes it to C<Parrot_ex_throw_from_c>.
480 
481 See also C<Parrot_ex_throw_from_op> and C<src/exit.c>.
482 
483 =cut
484 
485 */
486 
487 PARROT_EXPORT
488 PARROT_DOES_NOT_RETURN
489 PARROT_COLD
490 void
Parrot_ex_throw_from_c_args(PARROT_INTERP,ARGIN_NULLOK (SHIM (void * ret_addr)),int exitcode,ARGIN (const char * format),...)491 Parrot_ex_throw_from_c_args(PARROT_INTERP, ARGIN_NULLOK(SHIM(void *ret_addr)),
492         int exitcode, ARGIN(const char *format), ...)
493 {
494     ASSERT_ARGS(Parrot_ex_throw_from_c_args)
495     PMC *exception;
496 
497     va_list arglist;
498     va_start(arglist, format);
499     exception = build_exception_from_args(interp, exitcode, format, arglist);
500     va_end(arglist);
501 
502     Parrot_ex_throw_from_c(interp, exception);
503 }
504 
505 /*
506 
507 =item C<opcode_t * Parrot_ex_rethrow_from_op(PARROT_INTERP, PMC *exception)>
508 
509 Rethrow the given exception from an op, if it has previously been thrown and
510 not handled by the provided handler. Marks the exception object as being
511 unhandled and throws it again.
512 
513 =cut
514 
515 */
516 
517 PARROT_EXPORT
518 PARROT_WARN_UNUSED_RESULT
519 PARROT_CAN_RETURN_NULL
520 opcode_t *
Parrot_ex_rethrow_from_op(PARROT_INTERP,ARGIN (PMC * exception))521 Parrot_ex_rethrow_from_op(PARROT_INTERP, ARGIN(PMC *exception))
522 {
523     ASSERT_ARGS(Parrot_ex_rethrow_from_op)
524     Parrot_ex_update_for_rethrow(interp, exception);
525     return Parrot_ex_throw_from_op(interp, exception, NULL);
526 }
527 
528 /*
529 
530 =item C<void Parrot_ex_rethrow_from_c(PARROT_INTERP, PMC *exception)>
531 
532 Rethrow the exception from C code. Marks the Exception PMC as being unhandled
533 and throws it again.
534 
535 =cut
536 
537 */
538 
539 PARROT_EXPORT
540 PARROT_DOES_NOT_RETURN
541 PARROT_COLD
542 void
Parrot_ex_rethrow_from_c(PARROT_INTERP,ARGIN (PMC * exception))543 Parrot_ex_rethrow_from_c(PARROT_INTERP, ARGIN(PMC *exception))
544 {
545     ASSERT_ARGS(Parrot_ex_rethrow_from_c)
546     Parrot_ex_update_for_rethrow(interp, exception);
547     Parrot_ex_throw_from_c(interp, exception);
548 }
549 
550 /*
551 
552 =item C<void Parrot_ex_mark_unhandled(PARROT_INTERP, PMC *exception)>
553 
554 Mark an exception as unhandled, as part of rethrowing it.
555 
556 =cut
557 
558 */
559 
560 PARROT_EXPORT
561 void
Parrot_ex_mark_unhandled(PARROT_INTERP,ARGIN (PMC * exception))562 Parrot_ex_mark_unhandled(PARROT_INTERP, ARGIN(PMC *exception))
563 {
564     ASSERT_ARGS(Parrot_ex_mark_unhandled)
565     VTABLE_set_integer_keyed_str(interp, exception, CONST_STRING(interp, "handled"), -1);
566 }
567 
568 /*
569 
570 =item C<PMC * Parrot_ex_get_current_handler(PARROT_INTERP, PMC *expmc)>
571 
572 Get the current exception handler from expmc.
573 If expmc is an exception handler, return itself.
574 If it's an exception, return its active handler.
575 
576 =back
577 
578 =cut
579 
580 */
581 
582 PARROT_EXPORT
583 PARROT_WARN_UNUSED_RESULT
584 PARROT_CAN_RETURN_NULL
585 PMC *
Parrot_ex_get_current_handler(PARROT_INTERP,ARGIN_NULLOK (PMC * expmc))586 Parrot_ex_get_current_handler(PARROT_INTERP, ARGIN_NULLOK(PMC *expmc))
587 {
588     ASSERT_ARGS(Parrot_ex_get_current_handler)
589     PMC *eh = PMCNULL;
590     if (!PMC_IS_NULL(expmc)) {
591         /* If isa ExceptionHandler, use it. If isa Exception, get its active handler */
592         if (expmc->vtable->base_type == enum_class_Exception)
593             GETATTR_Exception_handler(interp, expmc, eh);
594         else if (VTABLE_isa(interp, expmc, CONST_STRING(interp, "ExceptionHandler")))
595             eh = expmc;
596         else if (VTABLE_isa(interp, expmc, CONST_STRING(interp, "Exception")))
597             eh = VTABLE_get_attr_str(interp, expmc, CONST_STRING(interp, "handler"));
598     }
599     return eh;
600 }
601 
602 /*
603 
604 =head2 Error Functions
605 
606 =over 4
607 
608 =item C<void Parrot_assert(INTVAL condition, const char *condition_string, const
609 char *file, unsigned int line)>
610 
611 A better version of assert() that gives a backtrace.
612 
613 =cut
614 
615 */
616 
617 PARROT_EXPORT
618 PARROT_DOES_NOT_RETURN_WHEN_FALSE
619 void
Parrot_assert(INTVAL condition,ARGIN (const char * condition_string),ARGIN (const char * file),unsigned int line)620 Parrot_assert(INTVAL condition, ARGIN(const char *condition_string),
621         ARGIN(const char *file), unsigned int line)
622 {
623     ASSERT_ARGS(Parrot_assert)
624     if (!condition)
625         Parrot_confess(condition_string, file, line);
626 }
627 
628 /*
629 
630 =item C<void Parrot_confess(const char *cond, const char *file, unsigned int
631 line)>
632 
633 Prints a backtrace and message for a failed assertion.
634 
635 =cut
636 
637 */
638 
639 PARROT_EXPORT
640 PARROT_DOES_NOT_RETURN
641 PARROT_COLD
642 void
Parrot_confess(ARGIN (const char * cond),ARGIN (const char * file),unsigned int line)643 Parrot_confess(ARGIN(const char *cond), ARGIN(const char *file), unsigned int line)
644 {
645     ASSERT_ARGS(Parrot_confess)
646     fprintf(stderr, "%s:%u: failed assertion '%s'\n", file, line, cond);
647     Parrot_print_backtrace();
648     abort();
649 }
650 
651 /*
652 
653 =item C<void Parrot_print_backtrace(void)>
654 
655 Displays the primrose path to disaster, (the stack frames leading up to the
656 abort).  Used by C<Parrot_confess>.
657 
658 =cut
659 
660 */
661 
662 void
Parrot_print_backtrace(void)663 Parrot_print_backtrace(void)
664 {
665     ASSERT_ARGS(Parrot_print_backtrace)
666 #ifdef PARROT_HAS_BACKTRACE
667 #  define BACKTRACE_DEPTH 32
668 /*#  define BACKTRACE_VERBOSE */
669 #  ifndef PARROT_HAS_DLINFO
670 #    define BACKTRACE_VERBOSE
671 #  endif
672     Interp *emergency_interp = Parrot_interp_get_emergency_interpreter();
673     /* stolen from http://www.delorie.com/gnu/docs/glibc/libc_665.html */
674     void *array[BACKTRACE_DEPTH];
675     int i;
676 
677     const int size = backtrace(array, BACKTRACE_DEPTH);
678 
679     fprintf(stderr,
680             "Backtrace - Obtained %d stack frames (max trace depth is %d).\n",
681             size, BACKTRACE_DEPTH);
682 #  ifndef BACKTRACE_VERBOSE
683     for (i = 0; i < size; ++i) {
684         Dl_info   frameInfo;
685         const int found  = dladdr(array[i], &frameInfo);
686 
687         /* always indent */
688         const int indent = 2 + (2 * i);
689 
690         fprintf(stderr, "%*s", indent, "");
691 
692         if (found && frameInfo.dli_sname)
693             fprintf(stderr, "%s\n", frameInfo.dli_sname);
694         else
695             fprintf(stderr, "(unknown)\n");
696     }
697 
698 #  else
699     { /* Scope for strings */
700         char ** strings = backtrace_symbols(array, size);
701         if (strings) {
702             for (i = 0; i < size; ++i)
703                 fprintf(stderr, "%s\n", strings[i]);
704             /* backtrace_symbols gets memory using malloc */
705             free(strings);
706         }
707         else
708             fputs("Not enough memory for backtrace_symbols\n", stderr);
709     }
710 #  endif
711     fprintf(stderr, "Attempting to get PIR backtrace.  No guarantees.  Here goes...\n");
712     if (emergency_interp) {
713         Parrot_interp_clear_emergency_interpreter();
714         PDB_backtrace(emergency_interp);
715     }
716 #  undef BACKTRACE_DEPTH
717 #endif /* ifdef PARROT_HAS_BACKTRACE */
718 }
719 
720 /*
721 
722 =item C<static void Parrot_ex_update_for_rethrow(PARROT_INTERP, PMC * ex)>
723 
724 Update an exception PMC so that it can be rethrown.
725 
726 =cut
727 
728 */
729 
730 static void
Parrot_ex_update_for_rethrow(PARROT_INTERP,ARGMOD (PMC * ex))731 Parrot_ex_update_for_rethrow(PARROT_INTERP, ARGMOD(PMC * ex))
732 {
733     ASSERT_ARGS(Parrot_ex_update_for_rethrow)
734     STRING * const bt_strings_str = CONST_STRING(interp, "bt_strings");
735     PMC * bt_strings = VTABLE_get_attr_str(interp, ex, bt_strings_str);
736     STRING * const prev_backtrace = Parrot_dbg_get_exception_backtrace(interp, ex);
737 
738     if (PMC_IS_NULL(bt_strings)) {
739         bt_strings = Parrot_pmc_new(interp, enum_class_ResizableStringArray);
740         VTABLE_set_attr_str(interp, ex, bt_strings_str, bt_strings);
741     }
742     VTABLE_push_string(interp, bt_strings, prev_backtrace);
743 
744     Parrot_ex_mark_unhandled(interp, ex);
745 }
746 
747 /*
748 
749 =item C<STRING * Parrot_ex_build_complete_backtrace_string(PARROT_INTERP, PMC *
750 ex)>
751 
752 Get a complete backtrace string for an exception PMC, including backtraces
753 from all previous rethrow points.
754 
755 =cut
756 
757 */
758 
759 PARROT_CANNOT_RETURN_NULL
760 STRING *
Parrot_ex_build_complete_backtrace_string(PARROT_INTERP,ARGIN (PMC * ex))761 Parrot_ex_build_complete_backtrace_string(PARROT_INTERP, ARGIN(PMC * ex))
762 {
763     ASSERT_ARGS(Parrot_ex_build_complete_backtrace_string)
764     STRING * const cur_bt = Parrot_dbg_get_exception_backtrace(interp, ex);
765     PMC * const all_bt = VTABLE_get_attr_str(interp, ex, CONST_STRING(interp, "bt_strings"));
766     INTVAL elems, i;
767     PMC * builder;
768     if (PMC_IS_NULL(all_bt))
769         return cur_bt;
770 
771     elems = VTABLE_elements(interp, all_bt);
772     builder = Parrot_pmc_new(interp, enum_class_StringBuilder);
773     VTABLE_push_string(interp, builder, cur_bt);
774     for (i = elems - 1; i >= 0; i--) {
775         STRING * const i_bt = VTABLE_get_string_keyed_int(interp, all_bt, i);
776         if (STRING_IS_NULL(i_bt))
777             continue;
778         VTABLE_push_string(interp, builder, CONST_STRING(interp, "\nthrown from:\n"));
779         VTABLE_push_string(interp, builder, i_bt);
780     }
781     return VTABLE_get_string(interp, builder);
782 }
783 
784 
785 /*
786 
787 =back
788 
789 =head1 SEE ALSO
790 
791 F<include/parrot/exceptions.h>.
792 
793 =cut
794 
795 */
796 
797 
798 /*
799  * Local variables:
800  *   c-file-style: "parrot"
801  * End:
802  * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
803  */
804