Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * llvmjit_error.cpp 4 : * LLVM error related handling that requires interfacing with C++ 5 : * 6 : * Unfortunately neither (re)setting the C++ new handler, nor the LLVM OOM 7 : * handler are exposed to C. Therefore this file wraps the necessary code. 8 : * 9 : * Copyright (c) 2016-2024, PostgreSQL Global Development Group 10 : * 11 : * IDENTIFICATION 12 : * src/backend/jit/llvm/llvmjit_error.cpp 13 : * 14 : *------------------------------------------------------------------------- 15 : */ 16 : 17 : extern "C" 18 : { 19 : #include "postgres.h" 20 : } 21 : 22 : #include <llvm/Support/ErrorHandling.h> 23 : 24 : #include "jit/llvmjit.h" 25 : 26 : #include <new> 27 : 28 : static int fatal_new_handler_depth = 0; 29 : static std::new_handler old_new_handler = NULL; 30 : 31 : static void fatal_system_new_handler(void); 32 : static void fatal_llvm_new_handler(void *user_data, const char *reason, bool gen_crash_diag); 33 : static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag); 34 : 35 : 36 : /* 37 : * Enter a section in which C++ and LLVM errors are treated as FATAL errors. 38 : * 39 : * This is necessary for LLVM as LLVM's error handling for such cases 40 : * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort()) 41 : * isn't compatible with postgres error handling. Thus in sections where LLVM 42 : * code, not LLVM generated functions!, is executing, standard new, LLVM OOM 43 : * and LLVM fatal errors (some OOM errors masquerade as those) are redirected 44 : * to our own error handlers. 45 : * 46 : * These error handlers use FATAL, because there's no reliable way from within 47 : * LLVM to throw an error that's guaranteed not to corrupt LLVM's state. 48 : * 49 : * To avoid disturbing extensions using C++ and/or LLVM, these handlers are 50 : * unset when not executing LLVM code. There is no need to call 51 : * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the 52 : * handlers in that case. 53 : */ 54 : void 55 15140 : llvm_enter_fatal_on_oom(void) 56 : { 57 15140 : if (fatal_new_handler_depth == 0) 58 : { 59 15140 : old_new_handler = std::set_new_handler(fatal_system_new_handler); 60 15140 : llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler); 61 15140 : llvm::install_fatal_error_handler(fatal_llvm_error_handler); 62 : } 63 15140 : fatal_new_handler_depth++; 64 15140 : } 65 : 66 : /* 67 : * Leave fatal error section started with llvm_enter_fatal_on_oom(). 68 : */ 69 : void 70 15140 : llvm_leave_fatal_on_oom(void) 71 : { 72 15140 : fatal_new_handler_depth--; 73 15140 : if (fatal_new_handler_depth == 0) 74 : { 75 15140 : std::set_new_handler(old_new_handler); 76 15140 : llvm::remove_bad_alloc_error_handler(); 77 15140 : llvm::remove_fatal_error_handler(); 78 : } 79 15140 : } 80 : 81 : /* 82 : * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case 83 : * of errors. 84 : */ 85 : bool 86 470 : llvm_in_fatal_on_oom(void) 87 : { 88 470 : return fatal_new_handler_depth > 0; 89 : } 90 : 91 : /* 92 : * Reset fatal error handling. This should only be called in error recovery 93 : * loops like PostgresMain()'s. 94 : */ 95 : void 96 2854 : llvm_reset_after_error(void) 97 : { 98 2854 : if (fatal_new_handler_depth != 0) 99 : { 100 0 : std::set_new_handler(old_new_handler); 101 0 : llvm::remove_bad_alloc_error_handler(); 102 0 : llvm::remove_fatal_error_handler(); 103 : } 104 2854 : fatal_new_handler_depth = 0; 105 2854 : } 106 : 107 : void 108 20570 : llvm_assert_in_fatal_section(void) 109 : { 110 : Assert(fatal_new_handler_depth > 0); 111 20570 : } 112 : 113 : static void 114 0 : fatal_system_new_handler(void) 115 : { 116 0 : ereport(FATAL, 117 : (errcode(ERRCODE_OUT_OF_MEMORY), 118 : errmsg("out of memory"), 119 : errdetail("while in LLVM"))); 120 : } 121 : 122 : static void 123 0 : fatal_llvm_new_handler(void *user_data, 124 : const char *reason, 125 : bool gen_crash_diag) 126 : { 127 0 : ereport(FATAL, 128 : (errcode(ERRCODE_OUT_OF_MEMORY), 129 : errmsg("out of memory"), 130 : errdetail("While in LLVM: %s", reason))); 131 : } 132 : 133 : static void 134 0 : fatal_llvm_error_handler(void *user_data, 135 : const char *reason, 136 : bool gen_crash_diag) 137 : { 138 0 : ereport(FATAL, 139 : (errcode(ERRCODE_OUT_OF_MEMORY), 140 : errmsg("fatal llvm error: %s", reason))); 141 : }