ocaml/runtime/printexc.c

163 lines
5.1 KiB
C
Raw Normal View History

/**************************************************************************/
/* */
/* OCaml */
/* */
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
/* */
/* Copyright 1996 Institut National de Recherche en Informatique et */
/* en Automatique. */
/* */
/* All rights reserved. This file is distributed under the terms of */
/* the GNU Lesser General Public License version 2.1, with the */
/* special exception on linking described in the file LICENSE. */
/* */
/**************************************************************************/
#define CAML_INTERNALS
/* Print an uncaught exception and abort */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "caml/backtrace.h"
#include "caml/callback.h"
#include "caml/debugger.h"
#include "caml/fail.h"
#include "caml/misc.h"
#include "caml/mlvalues.h"
#include "caml/printexc.h"
#include "caml/memory.h"
#include "caml/memprof.h"
struct stringbuf {
char * ptr;
char * end;
char data[256];
};
static void add_char(struct stringbuf *buf, char c)
{
if (buf->ptr < buf->end) *(buf->ptr++) = c;
}
static void add_string(struct stringbuf *buf, const char *s)
{
Cleaning up the C code (#1812) Running Clang 6.0 and GCC 8 with full warnings on suggests a few simple improvements and clean-ups to the C code of OCaml. This commit implements them. * Remove old-style, unprototyped function declarations It's `int f(void)`, not `int f()`. [-Wstrict-prototypes] * Be more explicit about conversions involving `float` and `double` byterun/bigarray.c, byterun/ints.c: add explicit casts to clarify the intent renamed float field of conversion union from `d` to `f`. byterun/compact.c, byterun/gc_ctrl.c: some local variables were of type `float` while all FP computations here are done in double precision; turned these variables into `double`. [-Wdouble-promotion -Wfloat-conversion] *Add explicit initialization of struct field `compare_ext` [-Wmissing-field-initializers] * Declare more functions "noreturn" [-Wmissing-noreturn] * Make CAMLassert compliant with ISO C In `e1 ? e2 : e3`, expressions `e2` and `e3` must have the same type. `e2` of type `void` and `e3` of type `int`, as in the original code, is a GNU extension. * Remove or conditionalize unused macros Some macros were defined and never used. Some other macros were always defined but conditionally used. [-Wunused-macros] * Replace some uses of `int` by more appropriate types like `intnat` On a 64-bit platform, `int` is only 32 bits and may not represent correctly the length of a string or the size of an OCaml heap block. This commit replaces a number of uses of `int` by other types that are 64-bit wide on 64-bit architectures, such as `intnat` or `uintnat` or `size_t` or `mlsize_t`. Sometimes an `intnat` was used as an `int` and is intended as a Boolean (0 or 1); then it was replaced by an `int`. There are many remaining cases where we assign a 64-bit quantity to a 32-bit `int` variable. Either I believe these cases are safe (e.g. the 64-bit quantity is the difference between two pointers within an I/O buffer, something that always fits in 32 bits), or the code change was not obvious and too risky. [-Wshorten-64-to-32] * Put `inline` before return type `static inline void f(void)` is cleaner than `static void inline f(void)`. [-Wold-style-declaration] * Unused assignment to unused parameter Looks very useless. [-Wunused-but-set-parameter]
2018-06-07 03:55:09 -07:00
size_t len = strlen(s);
if (buf->ptr + len > buf->end) len = buf->end - buf->ptr;
if (len > 0) memmove(buf->ptr, s, len);
buf->ptr += len;
}
CAMLexport char * caml_format_exception(value exn)
{
mlsize_t start, i;
value bucket, v;
struct stringbuf buf;
char intbuf[64];
char * res;
buf.ptr = buf.data;
buf.end = buf.data + sizeof(buf.data) - 1;
if (Tag_val(exn) == 0) {
add_string(&buf, String_val(Field(Field(exn, 0), 0)));
/* Check for exceptions in the style of Match_failure and Assert_failure */
if (Wosize_val(exn) == 2 &&
Is_block(Field(exn, 1)) &&
Tag_val(Field(exn, 1)) == 0 &&
caml_is_special_exception(Field(exn, 0))) {
bucket = Field(exn, 1);
start = 0;
} else {
bucket = exn;
start = 1;
}
add_char(&buf, '(');
for (i = start; i < Wosize_val(bucket); i++) {
if (i > start) add_string(&buf, ", ");
v = Field(bucket, i);
if (Is_long(v)) {
snprintf(intbuf, sizeof(intbuf),
"%" ARCH_INTNAT_PRINTF_FORMAT "d", Long_val(v));
add_string(&buf, intbuf);
} else if (Tag_val(v) == String_tag) {
add_char(&buf, '"');
add_string(&buf, String_val(v));
add_char(&buf, '"');
} else {
add_char(&buf, '_');
}
}
add_char(&buf, ')');
} else
add_string(&buf, String_val(Field(exn, 0)));
*buf.ptr = 0; /* Terminate string */
i = buf.ptr - buf.data + 1;
res = caml_stat_alloc_noexc(i);
if (res == NULL) return NULL;
memmove(res, buf.data, i);
return res;
}
#ifdef NATIVE_CODE
# define DEBUGGER_IN_USE 0
#else
# define DEBUGGER_IN_USE caml_debugger_in_use
#endif
/* Default C implementation in case the OCaml one is not registered. */
static void default_fatal_uncaught_exception(value exn)
{
char * msg;
const value * at_exit;
int saved_backtrace_active, saved_backtrace_pos;
/* Build a string representation of the exception */
msg = caml_format_exception(exn);
/* Perform "at_exit" processing, ignoring all exceptions that may
be triggered by this */
saved_backtrace_active = Caml_state->backtrace_active;
saved_backtrace_pos = Caml_state->backtrace_pos;
Caml_state->backtrace_active = 0;
at_exit = caml_named_value("Pervasives.do_at_exit");
if (at_exit != NULL) caml_callback_exn(*at_exit, Val_unit);
Caml_state->backtrace_active = saved_backtrace_active;
Caml_state->backtrace_pos = saved_backtrace_pos;
/* Display the uncaught exception */
fprintf(stderr, "Fatal error: exception %s\n", msg);
caml_stat_free(msg);
/* Display the backtrace if available */
if (Caml_state->backtrace_active && !DEBUGGER_IN_USE)
caml_print_exception_backtrace();
}
int caml_abort_on_uncaught_exn = 0; /* see afl.c */
void caml_fatal_uncaught_exception(value exn)
{
const value *handle_uncaught_exception;
handle_uncaught_exception =
caml_named_value("Printexc.handle_uncaught_exception");
/* If the callback allocates, memprof could be called. In this case,
memprof's callback could raise an exception while
[handle_uncaught_exception] is running, so that the printing of
the exception fails. */
caml_memprof_set_suspended(1);
if (handle_uncaught_exception != NULL)
/* [Printexc.handle_uncaught_exception] does not raise exception. */
caml_callback2(*handle_uncaught_exception, exn, Val_bool(DEBUGGER_IN_USE));
else
default_fatal_uncaught_exception(exn);
/* Terminate the process */
if (caml_abort_on_uncaught_exn) {
abort();
} else {
exit(2);
}
}