| /*------------------------------------------------------------------------- |
| * |
| * llvmjit_error.cpp |
| * LLVM error related handling that requires interfacing with C++ |
| * |
| * Unfortunately neither (re)setting the C++ new handler, nor the LLVM OOM |
| * handler are exposed to C. Therefore this file wraps the necessary code. |
| * |
| * Copyright (c) 2016-2023, PostgreSQL Global Development Group |
| * |
| * IDENTIFICATION |
| * src/backend/jit/llvm/llvmjit_error.cpp |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| extern "C" |
| { |
| #include "postgres.h" |
| } |
| |
| #include <llvm/Support/ErrorHandling.h> |
| |
| #include "jit/llvmjit.h" |
| |
| #include <new> |
| |
| static int fatal_new_handler_depth = 0; |
| static std::new_handler old_new_handler = NULL; |
| |
| static void fatal_system_new_handler(void); |
| #if LLVM_VERSION_MAJOR > 4 |
| static void fatal_llvm_new_handler(void *user_data, const char *reason, bool gen_crash_diag); |
| #if LLVM_VERSION_MAJOR < 14 |
| static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag); |
| #endif |
| #endif |
| static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag); |
| #if LLVM_VERSION_MAJOR < 14 |
| static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag); |
| #endif |
| |
| |
| /* |
| * Enter a section in which C++ and LLVM errors are treated as FATAL errors. |
| * |
| * This is necessary for LLVM as LLVM's error handling for such cases |
| * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort()) |
| * isn't compatible with postgres error handling. Thus in sections where LLVM |
| * code, not LLVM generated functions!, is executing, standard new, LLVM OOM |
| * and LLVM fatal errors (some OOM errors masquerade as those) are redirected |
| * to our own error handlers. |
| * |
| * These error handlers use FATAL, because there's no reliable way from within |
| * LLVM to throw an error that's guaranteed not to corrupt LLVM's state. |
| * |
| * To avoid disturbing extensions using C++ and/or LLVM, these handlers are |
| * unset when not executing LLVM code. There is no need to call |
| * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the |
| * handlers in that case. |
| */ |
| void |
| llvm_enter_fatal_on_oom(void) |
| { |
| if (fatal_new_handler_depth == 0) |
| { |
| old_new_handler = std::set_new_handler(fatal_system_new_handler); |
| #if LLVM_VERSION_MAJOR > 4 |
| llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler); |
| #endif |
| llvm::install_fatal_error_handler(fatal_llvm_error_handler); |
| } |
| fatal_new_handler_depth++; |
| } |
| |
| /* |
| * Leave fatal error section started with llvm_enter_fatal_on_oom(). |
| */ |
| void |
| llvm_leave_fatal_on_oom(void) |
| { |
| fatal_new_handler_depth--; |
| if (fatal_new_handler_depth == 0) |
| { |
| std::set_new_handler(old_new_handler); |
| #if LLVM_VERSION_MAJOR > 4 |
| llvm::remove_bad_alloc_error_handler(); |
| #endif |
| llvm::remove_fatal_error_handler(); |
| } |
| } |
| |
| /* |
| * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case |
| * of errors. |
| */ |
| bool |
| llvm_in_fatal_on_oom(void) |
| { |
| return fatal_new_handler_depth > 0; |
| } |
| |
| /* |
| * Reset fatal error handling. This should only be called in error recovery |
| * loops like PostgresMain()'s. |
| */ |
| void |
| llvm_reset_after_error(void) |
| { |
| if (fatal_new_handler_depth != 0) |
| { |
| std::set_new_handler(old_new_handler); |
| #if LLVM_VERSION_MAJOR > 4 |
| llvm::remove_bad_alloc_error_handler(); |
| #endif |
| llvm::remove_fatal_error_handler(); |
| } |
| fatal_new_handler_depth = 0; |
| } |
| |
| void |
| llvm_assert_in_fatal_section(void) |
| { |
| Assert(fatal_new_handler_depth > 0); |
| } |
| |
| static void |
| fatal_system_new_handler(void) |
| { |
| ereport(FATAL, |
| (errcode(ERRCODE_OUT_OF_MEMORY), |
| errmsg("out of memory"), |
| errdetail("while in LLVM"))); |
| } |
| |
| #if LLVM_VERSION_MAJOR > 4 |
| static void |
| fatal_llvm_new_handler(void *user_data, |
| const char *reason, |
| bool gen_crash_diag) |
| { |
| ereport(FATAL, |
| (errcode(ERRCODE_OUT_OF_MEMORY), |
| errmsg("out of memory"), |
| errdetail("While in LLVM: %s", reason))); |
| } |
| #if LLVM_VERSION_MAJOR < 14 |
| static void |
| fatal_llvm_new_handler(void *user_data, |
| const std::string& reason, |
| bool gen_crash_diag) |
| { |
| fatal_llvm_new_handler(user_data, reason.c_str(), gen_crash_diag); |
| } |
| #endif |
| #endif |
| |
| static void |
| fatal_llvm_error_handler(void *user_data, |
| const char *reason, |
| bool gen_crash_diag) |
| { |
| ereport(FATAL, |
| (errcode(ERRCODE_OUT_OF_MEMORY), |
| errmsg("fatal llvm error: %s", reason))); |
| } |
| |
| #if LLVM_VERSION_MAJOR < 14 |
| static void |
| fatal_llvm_error_handler(void *user_data, |
| const std::string& reason, |
| bool gen_crash_diag) |
| { |
| fatal_llvm_error_handler(user_data, reason.c_str(), gen_crash_diag); |
| } |
| #endif |