bt2: don't print previous causes in causes created from bt2._Error
[babeltrace.git] / src / bindings / python / bt2 / bt2 / native_bt_log_and_append_error.h
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
25 #include <stdbool.h>
26
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;
40 PyObject *py_bt_error_msg = NULL;
41 GString *gstr = NULL;
42
43 /* If this exception has a (Python) cause, handle that one first. */
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
52 py_exc_tb = PyException_GetTraceback(py_exc_value);
53
54 if (PyErr_GivenExceptionMatches(py_exc_value, py_mod_bt2_exc_error_type)) {
55 /*
56 * If the raised exception is a bt2._Error, restore the wrapped
57 * error.
58 */
59 PyObject *py_error_swig_ptr;
60 const bt_error *error;
61 int ret;
62
63 /*
64 * We never raise a bt2._Error with a (Python) cause: it should
65 * be the end of the chain.
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
83 Py_DECREF(py_error_swig_ptr);
84
85 BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(error);
86
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 }
97
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);
102
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 }
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);
138 Py_XDECREF(py_bt_error_msg);
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
204 void log_exception_and_maybe_append_cause(
205 int func_log_level,
206 int active_log_level,
207 bool append_error,
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
256 void loge_exception_append_cause_clear(const char *module_name, int active_log_level)
257 {
258 log_exception_and_maybe_append_cause(BT_LOG_ERROR, active_log_level,
259 true, NULL, NULL, NULL, module_name);
260 PyErr_Clear();
261 }
262
263 static inline
264 void logw_exception_clear(int active_log_level)
265 {
266 log_exception_and_maybe_append_cause(BT_LOG_WARNING, active_log_level,
267 false, NULL, NULL, NULL, NULL);
268 PyErr_Clear();
269 }
This page took 0.035328 seconds and 4 git commands to generate.