Commit | Line | Data |
---|---|---|
65e28173 PP |
1 | /* |
2 | * The MIT License (MIT) | |
3 | * | |
4 | * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com> | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
969c1d8a FD |
25 | #include <stdbool.h> |
26 | ||
65e28173 PP |
27 | #include "logging/comp-logging.h" |
28 | ||
29 | static | |
30 | void restore_current_thread_error_and_append_exception_chain_recursive( | |
31 | int active_log_level, PyObject *py_exc_value, | |
32 | bt_self_component_class *self_component_class, | |
33 | bt_self_component *self_component, | |
34 | bt_self_message_iterator *self_message_iterator, | |
35 | const char *module_name) | |
36 | { | |
37 | PyObject *py_exc_cause_value; | |
38 | PyObject *py_exc_type = NULL; | |
39 | PyObject *py_exc_tb = NULL; | |
b53cd768 | 40 | PyObject *py_bt_error_msg = NULL; |
65e28173 PP |
41 | GString *gstr = NULL; |
42 | ||
b53cd768 | 43 | /* If this exception has a (Python) cause, handle that one first. */ |
65e28173 PP |
44 | py_exc_cause_value = PyException_GetCause(py_exc_value); |
45 | if (py_exc_cause_value) { | |
46 | restore_current_thread_error_and_append_exception_chain_recursive( | |
47 | active_log_level, py_exc_cause_value, | |
48 | self_component_class, self_component, | |
49 | self_message_iterator, module_name); | |
50 | } | |
51 | ||
b53cd768 SM |
52 | py_exc_tb = PyException_GetTraceback(py_exc_value); |
53 | ||
65e28173 | 54 | if (PyErr_GivenExceptionMatches(py_exc_value, py_mod_bt2_exc_error_type)) { |
b53cd768 SM |
55 | /* |
56 | * If the raised exception is a bt2._Error, restore the wrapped | |
57 | * error. | |
58 | */ | |
65e28173 PP |
59 | PyObject *py_error_swig_ptr; |
60 | const bt_error *error; | |
61 | int ret; | |
62 | ||
63 | /* | |
b53cd768 SM |
64 | * We never raise a bt2._Error with a (Python) cause: it should |
65 | * be the end of the chain. | |
65e28173 PP |
66 | */ |
67 | BT_ASSERT(!py_exc_cause_value); | |
68 | ||
69 | /* | |
70 | * We steal the error object from the exception, to move | |
71 | * it back as the current thread's error. | |
72 | */ | |
73 | py_error_swig_ptr = PyObject_GetAttrString(py_exc_value, "_ptr"); | |
74 | BT_ASSERT(py_error_swig_ptr); | |
75 | ||
76 | ret = PyObject_SetAttrString(py_exc_value, "_ptr", Py_None); | |
77 | BT_ASSERT(ret == 0); | |
78 | ||
79 | ret = SWIG_ConvertPtr(py_error_swig_ptr, (void **) &error, | |
80 | SWIGTYPE_p_bt_error, 0); | |
81 | BT_ASSERT(ret == 0); | |
82 | ||
b53cd768 SM |
83 | Py_DECREF(py_error_swig_ptr); |
84 | ||
65e28173 PP |
85 | BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(error); |
86 | ||
b53cd768 SM |
87 | /* |
88 | * Append a cause with just the traceback and message, not the | |
89 | * full str() of the bt2._Error. We don't want the causes of | |
90 | * this bt2._Error to be included in the cause we create. | |
91 | */ | |
92 | gstr = bt_py_common_format_tb(py_exc_tb, active_log_level); | |
93 | if (!gstr) { | |
94 | /* bt_py_common_format_tb has already warned. */ | |
95 | goto end; | |
96 | } | |
65e28173 | 97 | |
b53cd768 SM |
98 | g_string_prepend(gstr, "Traceback (most recent call last):\n"); |
99 | ||
100 | py_bt_error_msg = PyObject_GetAttrString(py_exc_value, "_msg"); | |
101 | BT_ASSERT(py_bt_error_msg); | |
65e28173 | 102 | |
b53cd768 SM |
103 | g_string_append_printf(gstr, "\nbt2._Error: %s", |
104 | PyUnicode_AsUTF8(py_bt_error_msg)); | |
105 | } else { | |
106 | py_exc_type = PyObject_Type(py_exc_value); | |
107 | ||
108 | gstr = bt_py_common_format_exception(py_exc_type, py_exc_value, | |
109 | py_exc_tb, active_log_level, false); | |
110 | if (!gstr) { | |
111 | /* bt_py_common_format_exception has already warned. */ | |
112 | goto end; | |
113 | } | |
65e28173 PP |
114 | } |
115 | ||
116 | if (self_component_class) { | |
117 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS( | |
118 | self_component_class, "%s", gstr->str); | |
119 | } else if (self_component) { | |
120 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT( | |
121 | self_component, "%s", gstr->str); | |
122 | } else if (self_message_iterator) { | |
123 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( | |
124 | self_message_iterator, "%s", gstr->str); | |
125 | } else { | |
126 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( | |
127 | module_name, "%s", gstr->str); | |
128 | } | |
129 | ||
130 | end: | |
131 | if (gstr) { | |
132 | g_string_free(gstr, TRUE); | |
133 | } | |
134 | ||
135 | Py_XDECREF(py_exc_cause_value); | |
136 | Py_XDECREF(py_exc_type); | |
137 | Py_XDECREF(py_exc_tb); | |
b53cd768 | 138 | Py_XDECREF(py_bt_error_msg); |
65e28173 PP |
139 | } |
140 | ||
141 | /* | |
142 | * If you have the following code: | |
143 | * | |
144 | * try: | |
145 | * try: | |
146 | * something_that_raises_bt2_error() | |
147 | * except bt2._Error as e1: | |
148 | * raise ValueError from e1 | |
149 | * except ValueError as e2: | |
150 | * raise TypeError from e2 | |
151 | * | |
152 | * We will have the following exception chain: | |
153 | * | |
154 | * TypeError -> ValueError -> bt2._Error | |
155 | * | |
156 | * Where the TypeError is the current exception (obtained from PyErr_Fetch). | |
157 | * | |
158 | * The bt2._Error contains a `struct bt_error *` that used to be the current | |
159 | * thread's error, at the moment the exception was raised. | |
160 | * | |
161 | * This function gets to the bt2._Error and restores the wrapped | |
162 | * `struct bt_error *` as the current thread's error. | |
163 | * | |
164 | * Then, for each exception in the chain, starting with the oldest one, it adds | |
165 | * an error cause to the current thread's error. | |
166 | */ | |
167 | static | |
168 | void restore_bt_error_and_append_current_exception_chain( | |
169 | int active_log_level, | |
170 | bt_self_component_class *self_component_class, | |
171 | bt_self_component *self_component, | |
172 | bt_self_message_iterator *self_message_iterator, | |
173 | const char *module_name) | |
174 | { | |
175 | BT_ASSERT(PyErr_Occurred()); | |
176 | ||
177 | /* Used to access and restore the current exception. */ | |
178 | PyObject *py_exc_type; | |
179 | PyObject *py_exc_value; | |
180 | PyObject *py_exc_tb; | |
181 | ||
182 | /* Fetch and normalize the Python exception. */ | |
183 | PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb); | |
184 | PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb); | |
185 | BT_ASSERT(py_exc_type); | |
186 | BT_ASSERT(py_exc_value); | |
187 | BT_ASSERT(py_exc_tb); | |
188 | ||
189 | /* | |
190 | * Set the exception's traceback so it's possible to get it using | |
191 | * PyException_GetTraceback in | |
192 | * restore_current_thread_error_and_append_exception_chain_recursive. | |
193 | */ | |
194 | PyException_SetTraceback(py_exc_value, py_exc_tb); | |
195 | ||
196 | restore_current_thread_error_and_append_exception_chain_recursive( | |
197 | active_log_level, py_exc_value, self_component_class, | |
198 | self_component, self_message_iterator, module_name); | |
199 | ||
200 | PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb); | |
201 | } | |
202 | ||
203 | static inline | |
27ba49f1 SM |
204 | void log_exception_and_maybe_append_cause( |
205 | int func_log_level, | |
206 | int active_log_level, | |
207 | bool append_error, | |
65e28173 PP |
208 | bt_self_component_class *self_component_class, |
209 | bt_self_component *self_component, | |
210 | bt_self_message_iterator *self_message_iterator, | |
211 | const char *module_name) | |
212 | { | |
213 | GString *gstr; | |
214 | ||
215 | BT_ASSERT(PyErr_Occurred()); | |
216 | gstr = bt_py_common_format_current_exception(active_log_level); | |
217 | if (!gstr) { | |
218 | /* bt_py_common_format_current_exception() logs errors */ | |
219 | goto end; | |
220 | } | |
221 | ||
222 | BT_COMP_LOG_CUR_LVL(func_log_level, active_log_level, self_component, | |
223 | "%s", gstr->str); | |
224 | ||
225 | if (append_error) { | |
226 | restore_bt_error_and_append_current_exception_chain( | |
227 | active_log_level, self_component_class, self_component, | |
228 | self_message_iterator, module_name); | |
229 | ||
230 | } | |
231 | ||
232 | end: | |
233 | if (gstr) { | |
234 | g_string_free(gstr, TRUE); | |
235 | } | |
236 | } | |
237 | ||
238 | static | |
239 | bt_logging_level get_self_component_log_level(bt_self_component *self_comp) | |
240 | { | |
241 | return bt_component_get_logging_level( | |
242 | bt_self_component_as_component(self_comp)); | |
243 | } | |
244 | ||
245 | static | |
246 | bt_logging_level get_self_message_iterator_log_level( | |
247 | bt_self_message_iterator *self_msg_iter) | |
248 | { | |
249 | bt_self_component *self_comp = | |
250 | bt_self_message_iterator_borrow_component(self_msg_iter); | |
251 | ||
252 | return get_self_component_log_level(self_comp); | |
253 | } | |
254 | ||
255 | static inline | |
c7cc9a2a | 256 | void loge_exception_append_cause_clear(const char *module_name, int active_log_level) |
65e28173 | 257 | { |
27ba49f1 | 258 | log_exception_and_maybe_append_cause(BT_LOG_ERROR, active_log_level, |
65e28173 | 259 | true, NULL, NULL, NULL, module_name); |
c7cc9a2a | 260 | PyErr_Clear(); |
65e28173 PP |
261 | } |
262 | ||
65e28173 | 263 | static inline |
c7cc9a2a | 264 | void logw_exception_clear(int active_log_level) |
65e28173 | 265 | { |
27ba49f1 | 266 | log_exception_and_maybe_append_cause(BT_LOG_WARNING, active_log_level, |
65e28173 | 267 | false, NULL, NULL, NULL, NULL); |
c7cc9a2a | 268 | PyErr_Clear(); |
65e28173 | 269 | } |