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-2019, 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 | |
27 | static int fatal_new_handler_depth = 0; |
28 | static std::new_handler old_new_handler = NULL; |
29 | |
30 | static void fatal_system_new_handler(void); |
31 | #if LLVM_VERSION_MAJOR > 4 |
32 | static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag); |
33 | #endif |
34 | static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag); |
35 | |
36 | |
37 | /* |
38 | * Enter a section in which C++ and LLVM errors are treated as FATAL errors. |
39 | * |
40 | * This is necessary for LLVM as LLVM's error handling for such cases |
41 | * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort()) |
42 | * isn't compatible with postgres error handling. Thus in sections where LLVM |
43 | * code, not LLVM generated functions!, is executing, standard new, LLVM OOM |
44 | * and LLVM fatal errors (some OOM errors masquerade as those) are redirected |
45 | * to our own error handlers. |
46 | * |
47 | * These error handlers use FATAL, because there's no reliable way from within |
48 | * LLVM to throw an error that's guaranteed not to corrupt LLVM's state. |
49 | * |
50 | * To avoid disturbing extensions using C++ and/or LLVM, these handlers are |
51 | * unset when not executing LLVM code. There is no need to call |
52 | * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the |
53 | * handlers in that case. |
54 | */ |
55 | void |
56 | llvm_enter_fatal_on_oom(void) |
57 | { |
58 | if (fatal_new_handler_depth == 0) |
59 | { |
60 | old_new_handler = std::set_new_handler(fatal_system_new_handler); |
61 | #if LLVM_VERSION_MAJOR > 4 |
62 | llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler); |
63 | #endif |
64 | llvm::install_fatal_error_handler(fatal_llvm_error_handler); |
65 | } |
66 | fatal_new_handler_depth++; |
67 | } |
68 | |
69 | /* |
70 | * Leave fatal error section started with llvm_enter_fatal_on_oom(). |
71 | */ |
72 | void |
73 | llvm_leave_fatal_on_oom(void) |
74 | { |
75 | fatal_new_handler_depth--; |
76 | if (fatal_new_handler_depth == 0) |
77 | { |
78 | std::set_new_handler(old_new_handler); |
79 | #if LLVM_VERSION_MAJOR > 4 |
80 | llvm::remove_bad_alloc_error_handler(); |
81 | #endif |
82 | llvm::remove_fatal_error_handler(); |
83 | } |
84 | } |
85 | |
86 | /* |
87 | * Reset fatal error handling. This should only be called in error recovery |
88 | * loops like PostgresMain()'s. |
89 | */ |
90 | void |
91 | llvm_reset_after_error(void) |
92 | { |
93 | if (fatal_new_handler_depth != 0) |
94 | { |
95 | std::set_new_handler(old_new_handler); |
96 | #if LLVM_VERSION_MAJOR > 4 |
97 | llvm::remove_bad_alloc_error_handler(); |
98 | #endif |
99 | llvm::remove_fatal_error_handler(); |
100 | } |
101 | fatal_new_handler_depth = 0; |
102 | } |
103 | |
104 | void |
105 | llvm_assert_in_fatal_section(void) |
106 | { |
107 | Assert(fatal_new_handler_depth > 0); |
108 | } |
109 | |
110 | static void |
111 | fatal_system_new_handler(void) |
112 | { |
113 | ereport(FATAL, |
114 | (errcode(ERRCODE_OUT_OF_MEMORY), |
115 | errmsg("out of memory" ), |
116 | errdetail("while in LLVM" ))); |
117 | } |
118 | |
119 | #if LLVM_VERSION_MAJOR > 4 |
120 | static void |
121 | fatal_llvm_new_handler(void *user_data, |
122 | const std::string& reason, |
123 | bool gen_crash_diag) |
124 | { |
125 | ereport(FATAL, |
126 | (errcode(ERRCODE_OUT_OF_MEMORY), |
127 | errmsg("out of memory" ), |
128 | errdetail("While in LLVM: %s" , reason.c_str()))); |
129 | } |
130 | #endif |
131 | |
132 | static void |
133 | fatal_llvm_error_handler(void *user_data, |
134 | const std::string& reason, |
135 | bool gen_crash_diag) |
136 | { |
137 | ereport(FATAL, |
138 | (errcode(ERRCODE_OUT_OF_MEMORY), |
139 | errmsg("fatal llvm error: %s" , |
140 | reason.c_str()))); |
141 | } |
142 | |