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