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 : #if LLVM_VERSION_MAJOR < 14
34 : static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
35 : #endif
36 : static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag);
37 : #if LLVM_VERSION_MAJOR < 14
38 : static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
39 : #endif
40 :
41 :
42 : /*
43 : * Enter a section in which C++ and LLVM errors are treated as FATAL errors.
44 : *
45 : * This is necessary for LLVM as LLVM's error handling for such cases
46 : * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort())
47 : * isn't compatible with postgres error handling. Thus in sections where LLVM
48 : * code, not LLVM generated functions!, is executing, standard new, LLVM OOM
49 : * and LLVM fatal errors (some OOM errors masquerade as those) are redirected
50 : * to our own error handlers.
51 : *
52 : * These error handlers use FATAL, because there's no reliable way from within
53 : * LLVM to throw an error that's guaranteed not to corrupt LLVM's state.
54 : *
55 : * To avoid disturbing extensions using C++ and/or LLVM, these handlers are
56 : * unset when not executing LLVM code. There is no need to call
57 : * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the
58 : * handlers in that case.
59 : */
60 : void
61 18982 : llvm_enter_fatal_on_oom(void)
62 : {
63 18982 : if (fatal_new_handler_depth == 0)
64 : {
65 18982 : old_new_handler = std::set_new_handler(fatal_system_new_handler);
66 18982 : llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
67 18982 : llvm::install_fatal_error_handler(fatal_llvm_error_handler);
68 : }
69 18982 : fatal_new_handler_depth++;
70 18982 : }
71 :
72 : /*
73 : * Leave fatal error section started with llvm_enter_fatal_on_oom().
74 : */
75 : void
76 18982 : llvm_leave_fatal_on_oom(void)
77 : {
78 18982 : fatal_new_handler_depth--;
79 18982 : if (fatal_new_handler_depth == 0)
80 : {
81 18982 : std::set_new_handler(old_new_handler);
82 18982 : llvm::remove_bad_alloc_error_handler();
83 18982 : llvm::remove_fatal_error_handler();
84 : }
85 18982 : }
86 :
87 : /*
88 : * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case
89 : * of errors.
90 : */
91 : bool
92 798 : llvm_in_fatal_on_oom(void)
93 : {
94 798 : return fatal_new_handler_depth > 0;
95 : }
96 :
97 : /*
98 : * Reset fatal error handling. This should only be called in error recovery
99 : * loops like PostgresMain()'s.
100 : */
101 : void
102 3406 : llvm_reset_after_error(void)
103 : {
104 3406 : if (fatal_new_handler_depth != 0)
105 : {
106 0 : std::set_new_handler(old_new_handler);
107 0 : llvm::remove_bad_alloc_error_handler();
108 0 : llvm::remove_fatal_error_handler();
109 : }
110 3406 : fatal_new_handler_depth = 0;
111 3406 : }
112 :
113 : void
114 25744 : llvm_assert_in_fatal_section(void)
115 : {
116 : Assert(fatal_new_handler_depth > 0);
117 25744 : }
118 :
119 : static void
120 0 : fatal_system_new_handler(void)
121 : {
122 0 : ereport(FATAL,
123 : (errcode(ERRCODE_OUT_OF_MEMORY),
124 : errmsg("out of memory"),
125 : errdetail("while in LLVM")));
126 : }
127 :
128 : static void
129 0 : fatal_llvm_new_handler(void *user_data,
130 : const char *reason,
131 : bool gen_crash_diag)
132 : {
133 0 : ereport(FATAL,
134 : (errcode(ERRCODE_OUT_OF_MEMORY),
135 : errmsg("out of memory"),
136 : errdetail("While in LLVM: %s", reason)));
137 : }
138 : #if LLVM_VERSION_MAJOR < 14
139 : static void
140 0 : fatal_llvm_new_handler(void *user_data,
141 : const std::string& reason,
142 : bool gen_crash_diag)
143 : {
144 0 : fatal_llvm_new_handler(user_data, reason.c_str(), gen_crash_diag);
145 0 : }
146 : #endif
147 :
148 : static void
149 0 : fatal_llvm_error_handler(void *user_data,
150 : const char *reason,
151 : bool gen_crash_diag)
152 : {
153 0 : ereport(FATAL,
154 : (errcode(ERRCODE_OUT_OF_MEMORY),
155 : errmsg("fatal llvm error: %s", reason)));
156 : }
157 :
158 : #if LLVM_VERSION_MAJOR < 14
159 : static void
160 0 : fatal_llvm_error_handler(void *user_data,
161 : const std::string& reason,
162 : bool gen_crash_diag)
163 : {
164 0 : fatal_llvm_error_handler(user_data, reason.c_str(), gen_crash_diag);
165 0 : }
166 : #endif
|