Branch data 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 : 0 : llvm_enter_fatal_on_oom(void)
56 : : {
57 [ # # ]: 0 : if (fatal_new_handler_depth == 0)
58 : : {
59 : 0 : old_new_handler = std::set_new_handler(fatal_system_new_handler);
60 : 0 : llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
61 : 0 : llvm::install_fatal_error_handler(fatal_llvm_error_handler);
62 : : }
63 : 0 : fatal_new_handler_depth++;
64 : 0 : }
65 : :
66 : : /*
67 : : * Leave fatal error section started with llvm_enter_fatal_on_oom().
68 : : */
69 : : void
70 : 0 : llvm_leave_fatal_on_oom(void)
71 : : {
72 : 0 : fatal_new_handler_depth--;
73 [ # # ]: 0 : if (fatal_new_handler_depth == 0)
74 : : {
75 : 0 : std::set_new_handler(old_new_handler);
76 : 0 : llvm::remove_bad_alloc_error_handler();
77 : 0 : llvm::remove_fatal_error_handler();
78 : : }
79 : 0 : }
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 : 0 : llvm_in_fatal_on_oom(void)
87 : : {
88 : 0 : 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 : 0 : llvm_reset_after_error(void)
97 : : {
98 [ # # ]: 0 : 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 : 0 : fatal_new_handler_depth = 0;
105 : 0 : }
106 : :
107 : : void
108 : 0 : llvm_assert_in_fatal_section(void)
109 : : {
110 : : Assert(fatal_new_handler_depth > 0);
111 : 0 : }
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 : : }
|