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-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
62 {
63  if (fatal_new_handler_depth == 0)
64  {
65  old_new_handler = std::set_new_handler(fatal_system_new_handler);
66  llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
67  llvm::install_fatal_error_handler(fatal_llvm_error_handler);
68  }
70 }
71 
72 /*
73  * Leave fatal error section started with llvm_enter_fatal_on_oom().
74  */
75 void
77 {
79  if (fatal_new_handler_depth == 0)
80  {
81  std::set_new_handler(old_new_handler);
82  llvm::remove_bad_alloc_error_handler();
83  llvm::remove_fatal_error_handler();
84  }
85 }
86 
87 /*
88  * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case
89  * of errors.
90  */
91 bool
93 {
94  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
103 {
104  if (fatal_new_handler_depth != 0)
105  {
106  std::set_new_handler(old_new_handler);
107  llvm::remove_bad_alloc_error_handler();
108  llvm::remove_fatal_error_handler();
109  }
111 }
112 
113 void
115 {
117 }
118 
119 static void
121 {
122  ereport(FATAL,
123  (errcode(ERRCODE_OUT_OF_MEMORY),
124  errmsg("out of memory"),
125  errdetail("while in LLVM")));
126 }
127 
128 static void
129 fatal_llvm_new_handler(void *user_data,
130  const char *reason,
131  bool gen_crash_diag)
132 {
133  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 fatal_llvm_new_handler(void *user_data,
141  const std::string& reason,
142  bool gen_crash_diag)
143 {
144  fatal_llvm_new_handler(user_data, reason.c_str(), gen_crash_diag);
145 }
146 #endif
147 
148 static void
149 fatal_llvm_error_handler(void *user_data,
150  const char *reason,
151  bool gen_crash_diag)
152 {
153  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 fatal_llvm_error_handler(void *user_data,
161  const std::string& reason,
162  bool gen_crash_diag)
163 {
164  fatal_llvm_error_handler(user_data, reason.c_str(), gen_crash_diag);
165 }
166 #endif
#define Assert(condition)
Definition: c.h:858
int errdetail(const char *fmt,...)
Definition: elog.c:1205
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define FATAL
Definition: elog.h:41
#define ereport(elevel,...)
Definition: elog.h:149
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 void fatal_llvm_new_handler(void *user_data, const char *reason, bool gen_crash_diag)
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