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