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 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
56 {
57  if (fatal_new_handler_depth == 0)
58  {
59  old_new_handler = std::set_new_handler(fatal_system_new_handler);
60  llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
61  llvm::install_fatal_error_handler(fatal_llvm_error_handler);
62  }
64 }
65 
66 /*
67  * Leave fatal error section started with llvm_enter_fatal_on_oom().
68  */
69 void
71 {
73  if (fatal_new_handler_depth == 0)
74  {
75  std::set_new_handler(old_new_handler);
76  llvm::remove_bad_alloc_error_handler();
77  llvm::remove_fatal_error_handler();
78  }
79 }
80 
81 /*
82  * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case
83  * of errors.
84  */
85 bool
87 {
88  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
97 {
98  if (fatal_new_handler_depth != 0)
99  {
100  std::set_new_handler(old_new_handler);
101  llvm::remove_bad_alloc_error_handler();
102  llvm::remove_fatal_error_handler();
103  }
105 }
106 
107 void
109 {
111 }
112 
113 static void
115 {
116  ereport(FATAL,
117  (errcode(ERRCODE_OUT_OF_MEMORY),
118  errmsg("out of memory"),
119  errdetail("while in LLVM")));
120 }
121 
122 static void
123 fatal_llvm_new_handler(void *user_data,
124  const char *reason,
125  bool gen_crash_diag)
126 {
127  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 fatal_llvm_error_handler(void *user_data,
135  const char *reason,
136  bool gen_crash_diag)
137 {
138  ereport(FATAL,
139  (errcode(ERRCODE_OUT_OF_MEMORY),
140  errmsg("fatal llvm error: %s", reason)));
141 }
#define Assert(condition)
Definition: c.h:861
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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)