Commit | Line | Data |
---|---|---|
612a9870 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 | ||
25 | #include "logging/comp-logging.h" | |
26 | ||
27 | static | |
28 | void restore_current_thread_error_and_append_exception_chain_recursive( | |
29 | int active_log_level, PyObject *py_exc_value, | |
30 | bt_self_component_class *self_component_class, | |
31 | bt_self_component *self_component, | |
32 | bt_self_message_iterator *self_message_iterator, | |
33 | const char *module_name) | |
34 | { | |
35 | PyObject *py_exc_cause_value; | |
36 | PyObject *py_exc_type = NULL; | |
37 | PyObject *py_exc_tb = NULL; | |
38 | GString *gstr = NULL; | |
39 | ||
40 | /* If this exception has a cause, handle that one first. */ | |
41 | py_exc_cause_value = PyException_GetCause(py_exc_value); | |
42 | if (py_exc_cause_value) { | |
43 | restore_current_thread_error_and_append_exception_chain_recursive( | |
44 | active_log_level, py_exc_cause_value, | |
45 | self_component_class, self_component, | |
46 | self_message_iterator, module_name); | |
47 | } | |
48 | ||
49 | /* | |
50 | * If the raised exception is a bt2._Error, restore the wrapped error. | |
51 | */ | |
52 | if (PyErr_GivenExceptionMatches(py_exc_value, py_mod_bt2_exc_error_type)) { | |
53 | PyObject *py_error_swig_ptr; | |
54 | const bt_error *error; | |
55 | int ret; | |
56 | ||
57 | /* | |
58 | * We never raise a bt2._Error with a cause: it should be the | |
59 | * end of the chain. | |
60 | */ | |
61 | BT_ASSERT(!py_exc_cause_value); | |
62 | ||
63 | /* | |
64 | * We steal the error object from the exception, to move | |
65 | * it back as the current thread's error. | |
66 | */ | |
67 | py_error_swig_ptr = PyObject_GetAttrString(py_exc_value, "_ptr"); | |
68 | BT_ASSERT(py_error_swig_ptr); | |
69 | ||
70 | ret = PyObject_SetAttrString(py_exc_value, "_ptr", Py_None); | |
71 | BT_ASSERT(ret == 0); | |
72 | ||
73 | ret = SWIG_ConvertPtr(py_error_swig_ptr, (void **) &error, | |
74 | SWIGTYPE_p_bt_error, 0); | |
75 | BT_ASSERT(ret == 0); | |
76 | ||
77 | BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(error); | |
78 | ||
79 | Py_DECREF(py_error_swig_ptr); | |
80 | } | |
81 | ||
82 | py_exc_type = PyObject_Type(py_exc_value); | |
83 | py_exc_tb = PyException_GetTraceback(py_exc_value); | |
84 | ||
85 | gstr = bt_py_common_format_exception(py_exc_type, py_exc_value, | |
86 | py_exc_tb, active_log_level, false); | |
87 | if (!gstr) { | |
88 | /* bt_py_common_format_exception has already warned. */ | |
89 | goto end; | |
90 | } | |
91 | ||
92 | if (self_component_class) { | |
93 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS( | |
94 | self_component_class, "%s", gstr->str); | |
95 | } else if (self_component) { | |
96 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT( | |
97 | self_component, "%s", gstr->str); | |
98 | } else if (self_message_iterator) { | |
99 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( | |
100 | self_message_iterator, "%s", gstr->str); | |
101 | } else { | |
102 | BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( | |
103 | module_name, "%s", gstr->str); | |
104 | } | |
105 | ||
106 | end: | |
107 | if (gstr) { | |
108 | g_string_free(gstr, TRUE); | |
109 | } | |
110 | ||
111 | Py_XDECREF(py_exc_cause_value); | |
112 | Py_XDECREF(py_exc_type); | |
113 | Py_XDECREF(py_exc_tb); | |
114 | } | |
115 | ||
116 | /* | |
117 | * If you have the following code: | |
118 | * | |
119 | * try: | |
120 | * try: | |
121 | * something_that_raises_bt2_error() | |
122 | * except bt2._Error as e1: | |
123 | * raise ValueError from e1 | |
124 | * except ValueError as e2: | |
125 | * raise TypeError from e2 | |
126 | * | |
127 | * We will have the following exception chain: | |
128 | * | |
129 | * TypeError -> ValueError -> bt2._Error | |
130 | * | |
131 | * Where the TypeError is the current exception (obtained from PyErr_Fetch). | |
132 | * | |
133 | * The bt2._Error contains a `struct bt_error *` that used to be the current | |
134 | * thread's error, at the moment the exception was raised. | |
135 | * | |
136 | * This function gets to the bt2._Error and restores the wrapped | |
137 | * `struct bt_error *` as the current thread's error. | |
138 | * | |
139 | * Then, for each exception in the chain, starting with the oldest one, it adds | |
140 | * an error cause to the current thread's error. | |
141 | */ | |
142 | static | |
143 | void restore_bt_error_and_append_current_exception_chain( | |
144 | int active_log_level, | |
145 | bt_self_component_class *self_component_class, | |
146 | bt_self_component *self_component, | |
147 | bt_self_message_iterator *self_message_iterator, | |
148 | const char *module_name) | |
149 | { | |
150 | BT_ASSERT(PyErr_Occurred()); | |
151 | ||
152 | /* Used to access and restore the current exception. */ | |
153 | PyObject *py_exc_type; | |
154 | PyObject *py_exc_value; | |
155 | PyObject *py_exc_tb; | |
156 | ||
157 | /* Fetch and normalize the Python exception. */ | |
158 | PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb); | |
159 | PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb); | |
160 | BT_ASSERT(py_exc_type); | |
161 | BT_ASSERT(py_exc_value); | |
162 | BT_ASSERT(py_exc_tb); | |
163 | ||
164 | /* | |
165 | * Set the exception's traceback so it's possible to get it using | |
166 | * PyException_GetTraceback in | |
167 | * restore_current_thread_error_and_append_exception_chain_recursive. | |
168 | */ | |
169 | PyException_SetTraceback(py_exc_value, py_exc_tb); | |
170 | ||
171 | restore_current_thread_error_and_append_exception_chain_recursive( | |
172 | active_log_level, py_exc_value, self_component_class, | |
173 | self_component, self_message_iterator, module_name); | |
174 | ||
175 | PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb); | |
176 | } | |
177 | ||
178 | static inline | |
179 | void log_exception_and_maybe_append_error(int func_log_level, | |
180 | int active_log_level, bool append_error, | |
181 | bt_self_component_class *self_component_class, | |
182 | bt_self_component *self_component, | |
183 | bt_self_message_iterator *self_message_iterator, | |
184 | const char *module_name) | |
185 | { | |
186 | GString *gstr; | |
187 | ||
188 | BT_ASSERT(PyErr_Occurred()); | |
189 | gstr = bt_py_common_format_current_exception(active_log_level); | |
190 | if (!gstr) { | |
191 | /* bt_py_common_format_current_exception() logs errors */ | |
192 | goto end; | |
193 | } | |
194 | ||
195 | BT_COMP_LOG_CUR_LVL(func_log_level, active_log_level, self_component, | |
196 | "%s", gstr->str); | |
197 | ||
198 | if (append_error) { | |
199 | restore_bt_error_and_append_current_exception_chain( | |
200 | active_log_level, self_component_class, self_component, | |
201 | self_message_iterator, module_name); | |
202 | ||
203 | } | |
204 | ||
205 | end: | |
206 | if (gstr) { | |
207 | g_string_free(gstr, TRUE); | |
208 | } | |
209 | } | |
210 | ||
211 | static | |
212 | bt_logging_level get_self_component_log_level(bt_self_component *self_comp) | |
213 | { | |
214 | return bt_component_get_logging_level( | |
215 | bt_self_component_as_component(self_comp)); | |
216 | } | |
217 | ||
218 | static | |
219 | bt_logging_level get_self_message_iterator_log_level( | |
220 | bt_self_message_iterator *self_msg_iter) | |
221 | { | |
222 | bt_self_component *self_comp = | |
223 | bt_self_message_iterator_borrow_component(self_msg_iter); | |
224 | ||
225 | return get_self_component_log_level(self_comp); | |
226 | } | |
227 | ||
228 | static inline | |
229 | void loge_exception(const char *module_name, int active_log_level) | |
230 | { | |
231 | log_exception_and_maybe_append_error(BT_LOG_ERROR, active_log_level, | |
232 | true, NULL, NULL, NULL, module_name); | |
233 | } | |
234 | ||
612a9870 PP |
235 | static inline |
236 | void logw_exception(int active_log_level) | |
237 | { | |
238 | log_exception_and_maybe_append_error(BT_LOG_WARNING, active_log_level, | |
239 | false, NULL, NULL, NULL, NULL); | |
240 | } |