PostgreSQL Source Code  git master
llvmjit_error.cpp
Go to the documentation of this file.
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
64 {
65  if (fatal_new_handler_depth == 0)
66  {
67  old_new_handler = std::set_new_handler(fatal_system_new_handler);
68 #if LLVM_VERSION_MAJOR > 4
69  llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
70 #endif
71  llvm::install_fatal_error_handler(fatal_llvm_error_handler);
72  }
74 }
75 
76 /*
77  * Leave fatal error section started with llvm_enter_fatal_on_oom().
78  */
79 void
81 {
83  if (fatal_new_handler_depth == 0)
84  {
85  std::set_new_handler(old_new_handler);
86 #if LLVM_VERSION_MAJOR > 4
87  llvm::remove_bad_alloc_error_handler();
88 #endif
89  llvm::remove_fatal_error_handler();
90  }
91 }
92 
93 /*
94  * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case
95  * of errors.
96  */
97 bool
99 {
100  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
109 {
110  if (fatal_new_handler_depth != 0)
111  {
112  std::set_new_handler(old_new_handler);
113 #if LLVM_VERSION_MAJOR > 4
114  llvm::remove_bad_alloc_error_handler();
115 #endif
116  llvm::remove_fatal_error_handler();
117  }
119 }
120 
121 void
123 {
125 }
126 
127 static void
129 {
130  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 fatal_llvm_new_handler(void *user_data,
139  const char *reason,
140  bool gen_crash_diag)
141 {
142  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 fatal_llvm_error_handler(void *user_data,
160  const char *reason,
161  bool gen_crash_diag)
162 {
163  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
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define FATAL
Definition: elog.h:41
#define ereport(elevel,...)
Definition: elog.h:149
Assert(fmt[strlen(fmt) - 1] !='\n')
void llvm_enter_fatal_on_oom(void)
static std::new_handler old_new_handler
bool llvm_in_fatal_on_oom(void)
static void fatal_system_new_handler(void)
static int fatal_new_handler_depth
void llvm_assert_in_fatal_section(void)
void llvm_reset_after_error(void)
void llvm_leave_fatal_on_oom(void)
static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag)
char string[11]
Definition: preproc-type.c:52