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