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
CommitLineData
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
29static
30void 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
130end:
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 */
167static
168void 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
203static inline
27ba49f1
SM
204void 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
232end:
233 if (gstr) {
234 g_string_free(gstr, TRUE);
235 }
236}
237
238static
239bt_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
245static
246bt_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
255static inline
c7cc9a2a 256void 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 263static inline
c7cc9a2a 264void 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}
This page took 0.0449040000000001 seconds and 4 git commands to generate.