From: Philippe Proulx Date: Sat, 15 Jun 2019 20:48:07 +0000 (-0400) Subject: lib: add thread-safe error reporting API X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=553c4bab3cad8ad569c14a01b39a66a2d8bcde7c lib: add thread-safe error reporting API This patch adds an API for rich error reporting from an internal module (library, Python plugin provider, etc.), from a component, from a component class (query operation), from a message iterator, or from a graph listener function. The goal of this is, as a library user, to have access to an organized list of causes leading to a specific error when a library function fails. Those causes have module name and message properties, and can occur within user code. This is similar to a stack trace carried by an exception object in other languages. The objective is that a program (the CLI, for example) can print a report like this (module name between square brackets): ERROR: Graph failed to run completely: [ctf1 (stream0): src.ctf.fs] Cannot decode packet header for packet #546 in file `/path/to/stream/file`: invalid packet size (17 bits). CAUSED: [ctf1 (stream0): src.ctf.fs] Cannot get next message. CAUSED: [Babeltrace library] Message iterator's "next" method failed; port `port-name` of component `ctf1` (`src.ctf.fs`). CAUSED: [mux (out): flt.utils.muxer] Cannot get next upstream message iterator's message for input port `in3`. CAUSED: [Babeltrace library] Message iterator's "next" method failed; port `out` of component `mux` (`flt.utils.muxer`). CAUSED: [txt: sink.text.pretty] Cannot get next upstream message iterator's message. CAUSED: [Babeltrace library] Sink component's "consume" method failed. CAUSED: [Babeltrace library] Graph stopped running. The new concepts are: Error cause actor: The actor which is responsible for the cause of an error. The available actors are: Unknown: No specific actor (the library will use this, for example, as well as graph listeners). Component: The user code of a component caused the error. Component class: The user code of a component class (during a query operation) caused the error. Message iterator: The user code of a message iterator caused the error. Error cause: The cause of an error, that is, the context in which the error occured. An error cause has an actor type, a module name, a message, and, depending on its actor, other properties. For example, a single error cause could contain a whole Python stack trace as its message. The API function names start with `bt_error_cause`. Error: A list of error causes. The API function names start with `bt_error`. Current thread: Everything related to the current thread. The only available property is its error object. The API function names start with `bt_current_thread`. The library header `error-const.h` contains what you need to borrow the causes of an error object: bt_error_get_cause_count() returns the number of contained error causes, and with bt_error_borrow_cause_by_index() you can borrow a specific error cause. When you call a library function and it returns an error status, you MUST either: * Handle the error, and then continue. In that case, you need to clear the current thread's error object somehow. If you don't care about the causes of the error, you can call bt_current_thread_clear_error(). If you need to check the causes of the error, call bt_current_thread_take_error() which transfers the ownership of the current thread's error to you, clearing the thread's error at the same time. This is similar, for example, to how PyErr_Fetch() retrieves the error indicator and then clears it. Once you're done with the error object you now own, you can either: * Discard it with bt_error_release(). * Move it back to the current thread as its error object with bt_current_thread_move_error(). * Propagate the error, that is, return an error status yourself. Optionally, you can append an error cause (from your context) to the current thread's error object with one of the bt_current_thread_error_append_cause_from_*() functions or BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_*() macros (more details below). In that case, the error will be handled as explained above by one of your callers. The error cause API, depending on the error cause actor, offers the component name, component class name and type, plugin name (if available), and component output port name. I'm not comfortable keeping a weak reference on the component, component class, plugin, or message iterator object within the error cause object itself because the error could possibly outlive the component object. I'm also not comfortable keeping a strong reference because I'm pretty sure someone will forget to call bt_current_thread_clear_error() or bt_error_release() eventually and in that case it could cause important leaks. Leaking a few strings is not the end of the world, but if you leak the whole graph, then some finalization methods won't be called and you could have some serious incompleteness. You cannot append error causes to any error object because this would require taking the current thread's error object and then moving it back each time you need to append an error cause. Instead, you can append an error cause to the current thread's error specifically with one of: bt_current_thread_error_append_cause_status bt_current_thread_error_append_cause_from_unknown( const char *module_name, const char *file_name, uint64_t line_no, const char *msg_fmt, ...); bt_current_thread_error_append_cause_status bt_current_thread_error_append_cause_from_component( bt_self_component *self_comp, const char *file_name, uint64_t line_no, const char *msg_fmt, ...); bt_current_thread_error_append_cause_status bt_current_thread_error_append_cause_from_component_class( bt_self_component_class *self_comp_class, const char *file_name, uint64_t line_no, const char *msg_fmt, ...); bt_current_thread_error_append_cause_status bt_current_thread_error_append_cause_from_message_iterator( bt_self_message_iterator *self_iter, const char *file_name, uint64_t line_no, const char *msg_fmt, ...); #define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( _module_name, _msg_fmt, ...) #define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT( _self_comp, _msg_fmt, ...) #define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS( _self_cc, _msg_fmt, ...) #define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR( _self_iter, _msg_fmt, ...) The only available `bt_current_thread_error_append_cause_status` values are: * `BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_STATUS_OK` * `BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_STATUS_MEMORY_ERROR` The macro versions call the non-macro versions with `__FILE__` and `__LINE__` for the `file_name` and `line_no` parameters. The functions above work like printf() from their `msg_fmt` argument to set the error cause's message. For bt_current_thread_error_append_cause_from_unknown(), the module name can be anything. It should clearly identify the actor (for example, `Babeltrace library`, `Babeltrace CLI`, `Python bindings`). When the error cause's actor is not unknown, then the module name is conveniently set automatically: Component: `my-comp: src.ctf.fs` Component class: `src.ctf.fs` or `src.comp-cls` (no plugin) Message iterator: `my-comp (out2): src.ctf.fs` As of this patch, the projet does not use the new API. This work is reserved for subsequent patches. Signed-off-by: Philippe Proulx Change-Id: Ia791b2ff9bbf7a91e7c37416ba748c1186ee69a4 Reviewed-on: https://review.lttng.org/c/babeltrace/+/1474 Tested-by: jenkins --- diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 62dd8a9d..df4ae3cf 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -701,6 +701,10 @@ The available format specifiers are: |Plugin |`const struct bt_plugin *` +|`r` +|Error cause +|`const struct bt_error_cause *` + |`o` |Object pool |`+struct bt_object_pool *+` diff --git a/include/Makefile.am b/include/Makefile.am index 32084661..45071083 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -3,6 +3,9 @@ babeltrace2includedir = "$(includedir)/babeltrace2" babeltrace2include_HEADERS = \ babeltrace2/babeltrace.h \ babeltrace2/func-status.h \ + babeltrace2/current-thread.h \ + babeltrace2/error-cause-const.h \ + babeltrace2/error-const.h \ babeltrace2/logging.h \ babeltrace2/property.h \ babeltrace2/types.h \ diff --git a/include/babeltrace2/babeltrace.h b/include/babeltrace2/babeltrace.h index 646cff67..290b47a0 100644 --- a/include/babeltrace2/babeltrace.h +++ b/include/babeltrace2/babeltrace.h @@ -26,6 +26,9 @@ */ /* Core API */ +#include +#include +#include #include #include #include diff --git a/include/babeltrace2/current-thread.h b/include/babeltrace2/current-thread.h new file mode 100644 index 00000000..e89d0521 --- /dev/null +++ b/include/babeltrace2/current-thread.h @@ -0,0 +1,103 @@ +#ifndef BABELTRACE_CURRENT_THREAD_H +#define BABELTRACE_CURRENT_THREAD_H + +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +/* + * For bt_error, bt_self_component, bt_self_component_class, and + * bt_self_message_iterator + */ +#include + +/* For __BT_FUNC_STATUS_* */ +#define __BT_FUNC_STATUS_ENABLE +#include +#undef __BT_FUNC_STATUS_ENABLE + +#ifdef __cplusplus +extern "C" { +#endif + +extern +const bt_error *bt_current_thread_take_error(void); + +extern +void bt_current_thread_clear_error(void); + +extern +void bt_current_thread_move_error(const bt_error *error); + +typedef enum bt_current_thread_error_append_cause_status { + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_STATUS_MEMORY_ERROR = __BT_FUNC_STATUS_MEMORY_ERROR, + BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_STATUS_OK = __BT_FUNC_STATUS_OK, +} bt_current_thread_error_append_cause_status; + +extern +bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_unknown( + const char *module_name, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...); + +extern +bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_component( + bt_self_component *self_comp, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...); + +extern +bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_component_class( + bt_self_component_class *self_comp_class, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...); + +extern +bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_message_iterator( + bt_self_message_iterator *self_iter, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...); + +#define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(_module_name, _msg_fmt, ...) \ + bt_current_thread_error_append_cause_from_unknown( \ + (_module_name), __FILE__, __LINE__, (_msg_fmt), ##__VA_ARGS__) + +#define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(_self_comp, _msg_fmt, ...) \ + bt_current_thread_error_append_cause_from_component( \ + (_self_comp), __FILE__, __LINE__, (_msg_fmt), ##__VA_ARGS__) + +#define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS(_self_cc, _msg_fmt, ...) \ + bt_current_thread_error_append_cause_from_component_class( \ + (_self_cc), __FILE__, __LINE__, (_msg_fmt), ##__VA_ARGS__) + +#define BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_MESSAGE_ITERATOR(_self_iter, _msg_fmt, ...) \ + bt_current_thread_error_append_cause_from_component( \ + (_self_iter), __FILE__, __LINE__, (_msg_fmt), ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#include + +#endif /* BABELTRACE_CURRENT_THREAD_H */ diff --git a/include/babeltrace2/error-cause-const.h b/include/babeltrace2/error-cause-const.h new file mode 100644 index 00000000..472d5379 --- /dev/null +++ b/include/babeltrace2/error-cause-const.h @@ -0,0 +1,116 @@ +#ifndef BABELTRACE_ERROR_CAUSE_CONST_H +#define BABELTRACE_ERROR_CAUSE_CONST_H + +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +/* For bt_error, bt_error_cause */ +#include + +/* For bt_component_class_type */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum bt_error_cause_actor_type { + BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR, +} bt_error_cause_actor_type; + +extern +bt_error_cause_actor_type bt_error_cause_get_actor_type( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_get_message(const bt_error_cause *cause); + +extern +const char *bt_error_cause_get_module_name(const bt_error_cause *cause); + +extern +const char *bt_error_cause_get_file_name(const bt_error_cause *cause); + +extern +uint64_t bt_error_cause_get_line_number(const bt_error_cause *cause); + +extern +const char *bt_error_cause_component_actor_get_component_name( + const bt_error_cause *cause); + +extern +bt_component_class_type bt_error_cause_component_actor_get_component_class_type( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_component_actor_get_component_class_name( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_component_actor_get_plugin_name( + const bt_error_cause *cause); + +extern +bt_component_class_type +bt_error_cause_component_class_actor_get_component_class_type( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_component_class_actor_get_component_class_name( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_component_class_actor_get_plugin_name( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_message_iterator_actor_get_component_name( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_message_iterator_actor_get_component_output_port_name( + const bt_error_cause *cause); + +extern +bt_component_class_type +bt_error_cause_message_iterator_actor_get_component_class_type( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_message_iterator_actor_get_component_class_name( + const bt_error_cause *cause); + +extern +const char *bt_error_cause_message_iterator_actor_get_plugin_name( + const bt_error_cause *cause); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_ERROR_CAUSE_CONST_H */ diff --git a/include/babeltrace2/error-const.h b/include/babeltrace2/error-const.h new file mode 100644 index 00000000..0aaddb51 --- /dev/null +++ b/include/babeltrace2/error-const.h @@ -0,0 +1,50 @@ +#ifndef BABELTRACE_ERROR_CONST_H +#define BABELTRACE_ERROR_CONST_H + +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +/* For bt_error, bt_error_cause */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern +uint64_t bt_error_get_cause_count(const bt_error *error); + +extern +const bt_error_cause *bt_error_borrow_cause_by_index( + const bt_error *error, uint64_t index); + +extern +void bt_error_release(const bt_error *error); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_ERROR_CONST_H */ diff --git a/include/babeltrace2/types.h b/include/babeltrace2/types.h index 6884f175..86f6501a 100644 --- a/include/babeltrace2/types.h +++ b/include/babeltrace2/types.h @@ -99,6 +99,8 @@ typedef struct bt_connection bt_connection; typedef struct bt_event bt_event; typedef struct bt_event_class bt_event_class; typedef struct bt_event_header_field bt_event_header_field; +typedef struct bt_error bt_error; +typedef struct bt_error_cause bt_error_cause; typedef struct bt_field bt_field; typedef struct bt_field_class bt_field_class; typedef struct bt_field_class_enumeration_mapping bt_field_class_enumeration_mapping; @@ -124,6 +126,7 @@ typedef struct bt_port_output bt_port_output; typedef struct bt_port_output_message_iterator bt_port_output_message_iterator; typedef struct bt_query_executor bt_query_executor; typedef struct bt_self_component bt_self_component; +typedef struct bt_self_component_class bt_self_component_class; typedef struct bt_self_component_class_filter bt_self_component_class_filter; typedef struct bt_self_component_class_sink bt_self_component_class_sink; typedef struct bt_self_component_class_source bt_self_component_class_source; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index c00a0ee1..319f0fcc 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -6,6 +6,9 @@ libbabeltrace2_la_SOURCES = \ assert-post.h \ assert-pre.h \ babeltrace2.c \ + current-thread.c \ + error.c \ + error.h \ func-status.h \ lib-logging.c \ logging.c \ diff --git a/src/lib/current-thread.c b/src/lib/current-thread.c new file mode 100644 index 00000000..959e1695 --- /dev/null +++ b/src/lib/current-thread.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "LIB/CUR-THREAD" +#include "lib/logging.h" + +#include +#include +#include + +#include "error.h" +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "lib/func-status.h" + +/* + * This points to the thread's error object, or it's `NULL` if there's + * no current error object. + */ +static __thread struct bt_error *thread_error; + +const struct bt_error *bt_current_thread_take_error(void) +{ + struct bt_error *error = thread_error; + + thread_error = NULL; + BT_LOGD("Took current thread's error object: addr=%p", + error); + return error; +} + +void bt_current_thread_clear_error(void) +{ + bt_error_destroy(thread_error); + BT_LOGD("Cleared current thread's error object: addr=%p", + thread_error); + thread_error = NULL; +} + +void bt_current_thread_move_error(const struct bt_error *error) +{ + bt_current_thread_clear_error(); + thread_error = (void *) error; + BT_LOGD("Moved error object as current thread's error: addr=%p", + thread_error); +} + +/* + * Creates the current thread's error object if it does not already + * exist. + */ +static +int try_create_thread_error(void) +{ + int status = BT_FUNC_STATUS_OK; + + if (thread_error) { + goto end; + } + + BT_LOGD_STR("Creating current thread's error object."); + thread_error = bt_error_create(); + if (!thread_error) { + /* bt_error_create() logs errors */ + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + BT_LOGD("Created current thread's error object: addr=%p", thread_error); + +end: + return status; +} + +enum bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_unknown( + const char *module_name, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...) +{ + enum bt_current_thread_error_append_cause_status status = + try_create_thread_error(); + va_list args; + + if (status) { + goto end; + } + + BT_LOGD("Appending error cause to current thread's error from unknown actor: " + "error-addr=%p", thread_error); + va_start(args, msg_fmt); + status = bt_error_append_cause_from_unknown(thread_error, module_name, + file_name, line_no, msg_fmt, args); + va_end(args); + +end: + return status; +} + +enum bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_component( + bt_self_component *self_comp, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...) +{ + enum bt_current_thread_error_append_cause_status status = + try_create_thread_error(); + va_list args; + + if (status) { + goto end; + } + + BT_LOGD("Appending error cause to current thread's error from component: " + "error-addr=%p", thread_error); + va_start(args, msg_fmt); + status = bt_error_append_cause_from_component(thread_error, self_comp, + file_name, line_no, msg_fmt, args); + va_end(args); + +end: + return status; +} + +enum bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_component_class( + bt_self_component_class *self_comp_class, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...) +{ + enum bt_current_thread_error_append_cause_status status = + try_create_thread_error(); + va_list args; + + if (status) { + goto end; + } + + BT_LOGD("Appending error cause to current thread's error from component class actor: " + "error-addr=%p", thread_error); + va_start(args, msg_fmt); + status = bt_error_append_cause_from_component_class(thread_error, + self_comp_class, file_name, line_no, msg_fmt, args); + va_end(args); + +end: + return status; +} + +enum bt_current_thread_error_append_cause_status +bt_current_thread_error_append_cause_from_message_iterator( + bt_self_message_iterator *self_iter, const char *file_name, + uint64_t line_no, const char *msg_fmt, ...) +{ + enum bt_current_thread_error_append_cause_status status = + try_create_thread_error(); + va_list args; + + if (status) { + goto end; + } + + BT_LOGD("Appending error cause to current thread's error from message iterator actor: " + "error-addr=%p", thread_error); + va_start(args, msg_fmt); + status = bt_error_append_cause_from_message_iterator(thread_error, + self_iter, file_name, line_no, msg_fmt, args); + va_end(args); + +end: + return status; +} diff --git a/src/lib/error.c b/src/lib/error.c new file mode 100644 index 00000000..b19fe1da --- /dev/null +++ b/src/lib/error.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "LIB/ERROR" +#include "lib/logging.h" + +#include +#include +#include +#include + +#include "error.h" +#include "graph/message/iterator.h" +#include "graph/component.h" +#include "graph/component-class.h" +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "lib/func-status.h" + +#define BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(_cause, _exp_type) \ + BT_ASSERT_PRE(((const struct bt_error_cause *) (_cause))->actor_type == _exp_type, \ + "Unexpected error cause's actor type: type=%s, exp-type=%s", \ + bt_error_cause_actor_type_string(((const struct bt_error_cause *) (_cause))->actor_type), \ + bt_error_cause_actor_type_string(_exp_type)) + +static +void fini_component_class_id( + struct bt_error_cause_component_class_id *comp_class_id) +{ + BT_ASSERT(comp_class_id); + + if (comp_class_id->name) { + g_string_free(comp_class_id->name, TRUE); + comp_class_id->name = NULL; + } + + if (comp_class_id->plugin_name) { + g_string_free(comp_class_id->plugin_name, TRUE); + comp_class_id->plugin_name = NULL; + } +} + +static +void fini_error_cause(struct bt_error_cause *cause) +{ + BT_ASSERT(cause); + BT_LIB_LOGD("Finalizing error cause: %!+r", cause); + + if (cause->module_name) { + g_string_free(cause->module_name, TRUE); + cause->module_name = NULL; + } + + if (cause->file_name) { + g_string_free(cause->file_name, TRUE); + cause->file_name = NULL; + } + + if (cause->message) { + g_string_free(cause->message, TRUE); + cause->message = NULL; + } +} + +static +void destroy_error_cause(struct bt_error_cause *cause) +{ + if (!cause) { + goto end; + } + + BT_LIB_LOGD("Destroying error cause: %!+r", cause); + + switch (cause->actor_type) { + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT: + { + struct bt_error_cause_component_actor *spec_cause = + (void *) cause; + + if (spec_cause->comp_name) { + g_string_free(spec_cause->comp_name, TRUE); + spec_cause->comp_name = NULL; + } + + fini_component_class_id(&spec_cause->comp_class_id); + break; + } + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS: + { + struct bt_error_cause_component_class_actor *spec_cause = + (void *) cause; + + fini_component_class_id(&spec_cause->comp_class_id); + break; + } + case BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR: + { + struct bt_error_cause_message_iterator_actor *spec_cause = + (void *) cause; + + if (spec_cause->comp_name) { + g_string_free(spec_cause->comp_name, TRUE); + spec_cause->comp_name = NULL; + } + + if (spec_cause->output_port_name) { + g_string_free(spec_cause->output_port_name, TRUE); + spec_cause->output_port_name = NULL; + } + + fini_component_class_id(&spec_cause->comp_class_id); + break; + } + default: + break; + } + + fini_error_cause(cause); + g_free(cause); + +end: + return; +} + +static +int init_error_cause(struct bt_error_cause *cause, + enum bt_error_cause_actor_type actor_type) +{ + int ret = 0; + + BT_ASSERT(cause); + BT_LIB_LOGD("Initializing error cause: %!+r", cause); + cause->actor_type = actor_type; + cause->module_name = g_string_new(NULL); + if (!cause->module_name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + cause->message = g_string_new(NULL); + if (!cause->message) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + cause->file_name = g_string_new(NULL); + if (!cause->file_name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + BT_LIB_LOGD("Initialized error cause: %!+r", cause); + +end: + return ret; +} + +static +int init_component_class_id( + struct bt_error_cause_component_class_id *comp_class_id, + struct bt_component_class *comp_cls) +{ + int ret = 0; + + BT_ASSERT(comp_class_id); + comp_class_id->type = comp_cls->type; + comp_class_id->name = g_string_new(comp_cls->name->str); + if (!comp_class_id->name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + comp_class_id->plugin_name = g_string_new(comp_cls->plugin_name->str); + if (!comp_class_id->plugin_name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +void set_error_cause_props(struct bt_error_cause *cause, + const char *file_name, uint64_t line_no) +{ + BT_ASSERT(cause); + g_string_assign(cause->file_name, file_name); + cause->line_no = line_no; +} + +static +struct bt_error_cause *create_error_cause(const char *module_name, + const char *file_name, uint64_t line_no) +{ + struct bt_error_cause *cause = g_new0(struct bt_error_cause, 1); + int ret; + + BT_LOGD_STR("Creating error cause (unknown actor)."); + + if (!cause) { + BT_LOGE_STR("Failed to allocate one error cause."); + goto error; + } + + ret = init_error_cause(cause, BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN); + if (ret) { + goto error; + } + + g_string_assign(cause->module_name, module_name); + set_error_cause_props(cause, file_name, line_no); + BT_LIB_LOGD("Created error cause: %!+r", cause); + goto end; + +error: + destroy_error_cause(cause); + cause = NULL; + +end: + return cause; +} + +static +void append_component_class_id_str(GString *str, + struct bt_error_cause_component_class_id *comp_class_id) +{ + const char *type_str = NULL; + + switch (comp_class_id->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + type_str = "src"; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + type_str = "flt"; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + type_str = "sink"; + break; + default: + abort(); + } + + if (comp_class_id->plugin_name->len > 0) { + g_string_append_printf(str, "%s.%s.%s", + type_str, comp_class_id->plugin_name->str, + comp_class_id->name->str); + } else { + g_string_append_printf(str, "%s.%s", + type_str, comp_class_id->name->str); + } +} + +static +struct bt_error_cause_component_actor *create_error_cause_component_actor( + struct bt_component *comp, const char *file_name, + uint64_t line_no) +{ + struct bt_error_cause_component_actor *cause = + g_new0(struct bt_error_cause_component_actor, 1); + int ret; + + BT_LOGD_STR("Creating error cause object (component actor)."); + + if (!cause) { + goto error; + } + + ret = init_error_cause(&cause->base, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT); + if (ret) { + goto error; + } + + set_error_cause_props(&cause->base, file_name, line_no); + cause->comp_name = g_string_new(comp->name->str); + if (!cause->comp_name) { + BT_LOGE_STR("Failed to allocate one GString."); + goto error; + } + + ret = init_component_class_id(&cause->comp_class_id, comp->class); + if (ret) { + goto error; + } + + g_string_append_printf(cause->base.module_name, "%s: ", + comp->name->str); + append_component_class_id_str(cause->base.module_name, + &cause->comp_class_id); + BT_LIB_LOGD("Created error cause object: %!+r", cause); + goto end; + +error: + destroy_error_cause(&cause->base); + cause = NULL; + +end: + return cause; +} + +static +struct bt_error_cause_component_class_actor * +create_error_cause_component_class_actor(struct bt_component_class *comp_cls, + const char *file_name, uint64_t line_no) +{ + struct bt_error_cause_component_class_actor *cause = + g_new0(struct bt_error_cause_component_class_actor, 1); + int ret; + + BT_LOGD_STR("Creating error cause object (component class actor)."); + + if (!cause) { + BT_LOGE_STR("Failed to allocate one error cause object."); + goto error; + } + + ret = init_error_cause(&cause->base, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS); + if (ret) { + goto error; + } + + set_error_cause_props(&cause->base, file_name, line_no); + ret = init_component_class_id(&cause->comp_class_id, comp_cls); + if (ret) { + goto error; + } + + append_component_class_id_str(cause->base.module_name, + &cause->comp_class_id); + BT_LIB_LOGD("Created error cause object: %!+r", cause); + goto end; + +error: + destroy_error_cause(&cause->base); + cause = NULL; + +end: + return cause; +} + +struct bt_error_cause_message_iterator_actor * +create_error_cause_message_iterator_actor(struct bt_message_iterator *iter, + const char *file_name, uint64_t line_no) +{ + struct bt_error_cause_message_iterator_actor *cause; + struct bt_self_component_port_input_message_iterator *input_port_iter; + int ret; + + BT_LOGD_STR("Creating error cause object (message iterator actor)."); + + /* + * This can only be created from within a graph, from a user + * message iterator, which is a self component port input + * message iterator. + */ + BT_ASSERT(iter->type == + BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT); + input_port_iter = (void *) iter; + cause = g_new0(struct bt_error_cause_message_iterator_actor, 1); + if (!cause) { + BT_LOGE_STR("Failed to allocate one error cause object."); + goto error; + } + + ret = init_error_cause(&cause->base, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + if (ret) { + goto error; + } + + set_error_cause_props(&cause->base, file_name, line_no); + cause->comp_name = g_string_new( + input_port_iter->upstream_component->name->str); + if (!cause->comp_name) { + BT_LOGE_STR("Failed to allocate one GString."); + goto error; + } + + cause->output_port_name = g_string_new( + input_port_iter->upstream_port->name->str); + if (!cause->output_port_name) { + BT_LOGE_STR("Failed to allocate one GString."); + goto error; + } + + ret = init_component_class_id(&cause->comp_class_id, + input_port_iter->upstream_component->class); + if (ret) { + goto error; + } + + g_string_append_printf(cause->base.module_name, "%s (%s): ", + input_port_iter->upstream_component->name->str, + input_port_iter->upstream_port->name->str); + append_component_class_id_str(cause->base.module_name, + &cause->comp_class_id); + BT_LIB_LOGD("Created error cause object: %!+r", cause); + goto end; + +error: + destroy_error_cause(&cause->base); + cause = NULL; + +end: + return cause; +} + +BT_HIDDEN +struct bt_error *bt_error_create(void) +{ + struct bt_error *error; + + BT_LOGD_STR("Creating error object."); + error = g_new0(struct bt_error, 1); + if (!error) { + BT_LOGE_STR("Failed to allocate one error object."); + goto error; + } + + error->causes = g_ptr_array_new_with_free_func( + (GDestroyNotify) destroy_error_cause); + if (!error->causes) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + BT_LOGD("Created error object: addr=%p", error); + goto end; + +error: + bt_error_destroy(error); + error = NULL; + +end: + return error; +} + +BT_HIDDEN +void bt_error_destroy(struct bt_error *error) +{ + if (!error) { + goto end; + } + + if (error->causes) { + g_ptr_array_free(error->causes, TRUE); + error->causes = NULL; + } + + g_free(error); + +end: + return; +} + +BT_HIDDEN +int bt_error_append_cause_from_unknown(struct bt_error *error, + const char *module_name, const char *file_name, + uint64_t line_no, const char *msg_fmt, va_list args) +{ + struct bt_error_cause *cause = NULL; + int status = BT_FUNC_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(error, "Error"); + BT_ASSERT_PRE_NON_NULL(module_name, "Module name"); + BT_ASSERT_PRE_NON_NULL(file_name, "Function name"); + BT_ASSERT_PRE_NON_NULL(msg_fmt, "Message format string"); + BT_LOGD("Appending error cause from unknown actor: " + "module-name=\"%s\", func-name=\"%s\", line-no=%" PRIu64, + module_name, file_name, line_no); + cause = create_error_cause(module_name, file_name, line_no); + if (!cause) { + /* create_error_cause() logs errors */ + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + g_string_append_vprintf(cause->message, msg_fmt, args); + g_ptr_array_add(error->causes, cause); + BT_LIB_LOGD("Appended error cause: %!+r", cause); + cause = NULL; + +end: + destroy_error_cause(cause); + return status; +} + +BT_HIDDEN +int bt_error_append_cause_from_component( + struct bt_error *error, bt_self_component *self_comp, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args) +{ + struct bt_error_cause_component_actor *cause = NULL; + int status = BT_FUNC_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(error, "Error"); + BT_ASSERT_PRE_NON_NULL(self_comp, "Component"); + BT_ASSERT_PRE_NON_NULL(file_name, "Function name"); + BT_ASSERT_PRE_NON_NULL(msg_fmt, "Message format string"); + BT_LIB_LOGD("Appending error cause from component actor: %![comp-]+c", + self_comp); + cause = create_error_cause_component_actor((void *) self_comp, + file_name, line_no); + if (!cause) { + /* create_error_cause_component_actor() logs errors */ + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + g_string_append_vprintf(cause->base.message, msg_fmt, args); + g_ptr_array_add(error->causes, cause); + BT_LIB_LOGD("Appended error cause: %!+r", cause); + cause = NULL; + +end: + destroy_error_cause(&cause->base); + return status; +} + +BT_HIDDEN +int bt_error_append_cause_from_component_class( + struct bt_error *error, + bt_self_component_class *self_comp_class, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args) +{ + struct bt_error_cause_component_class_actor *cause = NULL; + int status = BT_FUNC_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(error, "Error"); + BT_ASSERT_PRE_NON_NULL(self_comp_class, "Component class"); + BT_ASSERT_PRE_NON_NULL(file_name, "Function name"); + BT_ASSERT_PRE_NON_NULL(msg_fmt, "Message format string"); + BT_LIB_LOGD("Appending error cause from component class actor: " + "%![comp-cls-]+C", self_comp_class); + cause = create_error_cause_component_class_actor( + (void *) self_comp_class, file_name, line_no); + if (!cause) { + /* create_error_cause_component_class_actor() logs errors */ + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + g_string_append_vprintf(cause->base.message, msg_fmt, args); + g_ptr_array_add(error->causes, cause); + BT_LIB_LOGD("Appended error cause: %!+r", cause); + cause = NULL; + +end: + destroy_error_cause(&cause->base); + return status; +} + +BT_HIDDEN +int bt_error_append_cause_from_message_iterator( + struct bt_error *error, bt_self_message_iterator *self_iter, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args) +{ + struct bt_error_cause_message_iterator_actor *cause = NULL; + int status = BT_FUNC_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(error, "Error"); + BT_ASSERT_PRE_NON_NULL(self_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(file_name, "Function name"); + BT_ASSERT_PRE_NON_NULL(msg_fmt, "Message format string"); + BT_LIB_LOGD("Appending error cause from message iterator actor: " + "%![comp-]+i", self_iter); + cause = create_error_cause_message_iterator_actor( + (void *) self_iter, file_name, line_no); + if (!cause) { + /* create_error_cause_message_iterator_actor() logs errors */ + status = BT_FUNC_STATUS_MEMORY_ERROR; + goto end; + } + + g_string_append_vprintf(cause->base.message, msg_fmt, args); + g_ptr_array_add(error->causes, cause); + BT_LIB_LOGD("Appended error cause: %!+r", cause); + cause = NULL; + +end: + destroy_error_cause(&cause->base); + return status; +} + +static +uint64_t error_cause_count(const bt_error *error) +{ + return error->causes ? error->causes->len : 0; +} + +uint64_t bt_error_get_cause_count(const bt_error *error) +{ + BT_ASSERT_PRE_NON_NULL(error, "Error"); + return error_cause_count(error); +} + +void bt_error_release(const struct bt_error *error) +{ + BT_ASSERT_PRE_NON_NULL(error, "Error"); + bt_error_destroy((void *) error); +} + +const struct bt_error_cause *bt_error_borrow_cause_by_index( + const bt_error *error, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(error, "Error"); + BT_ASSERT_PRE_VALID_INDEX(index, error_cause_count(error)); + return error->causes->pdata[index]; +} + +enum bt_error_cause_actor_type bt_error_cause_get_actor_type( + const struct bt_error_cause *cause) +{ + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + return cause->actor_type; +} + +const char *bt_error_cause_get_message(const struct bt_error_cause *cause) +{ + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + return cause->message->str; +} + +const char *bt_error_cause_get_module_name(const struct bt_error_cause *cause) +{ + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + return cause->module_name->str; +} + +const char *bt_error_cause_get_file_name(const struct bt_error_cause *cause) +{ + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + return cause->file_name->str; +} + +uint64_t bt_error_cause_get_line_number(const bt_error_cause *cause) +{ + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + return cause->line_no; +} + +const char *bt_error_cause_component_actor_get_component_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT); + return spec_cause->comp_name->str; +} + +bt_component_class_type bt_error_cause_component_actor_get_component_class_type( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT); + return spec_cause->comp_class_id.type; +} + +const char *bt_error_cause_component_actor_get_component_class_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT); + return spec_cause->comp_class_id.name->str; +} + +const char *bt_error_cause_component_actor_get_plugin_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT); + return spec_cause->comp_class_id.plugin_name->len > 0 ? + spec_cause->comp_class_id.plugin_name->str : NULL; +} + +bt_component_class_type +bt_error_cause_component_class_actor_get_component_class_type( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_class_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS); + return spec_cause->comp_class_id.type; +} + +const char *bt_error_cause_component_class_actor_get_component_class_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_class_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS); + return spec_cause->comp_class_id.name->str; +} + +const char *bt_error_cause_component_class_actor_get_plugin_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_class_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS); + return spec_cause->comp_class_id.plugin_name->len > 0 ? + spec_cause->comp_class_id.plugin_name->str : NULL; +} + +const char *bt_error_cause_message_iterator_actor_get_component_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + return spec_cause->comp_name->str; +} + +const char * +bt_error_cause_message_iterator_actor_get_component_output_port_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + return spec_cause->output_port_name->str; +} + +bt_component_class_type +bt_error_cause_message_iterator_actor_get_component_class_type( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + return spec_cause->comp_class_id.type; +} + +const char *bt_error_cause_message_iterator_actor_get_component_class_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + return spec_cause->comp_class_id.name->str; +} + +const char *bt_error_cause_message_iterator_actor_get_plugin_name( + const struct bt_error_cause *cause) +{ + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BT_ASSERT_PRE_NON_NULL(cause, "Error cause"); + BT_ASSERT_PRE_CAUSE_HAS_ACTOR_TYPE(cause, + BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR); + return spec_cause->comp_class_id.plugin_name->len > 0 ? + spec_cause->comp_class_id.plugin_name->str : NULL; +} diff --git a/src/lib/error.h b/src/lib/error.h new file mode 100644 index 00000000..e23494d2 --- /dev/null +++ b/src/lib/error.h @@ -0,0 +1,121 @@ +#ifndef BABELTRACE_ERROR_INTERNAL_H +#define BABELTRACE_ERROR_INTERNAL_H + +/* + * Copyright (c) 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "lib/object.h" +#include "common/macros.h" + +struct bt_error_cause { + enum bt_error_cause_actor_type actor_type; + GString *module_name; + GString *message; + GString *file_name; + uint64_t line_no; +}; + +struct bt_error_cause_component_class_id { + GString *name; + bt_component_class_type type; + GString *plugin_name; +}; + +struct bt_error_cause_component_actor { + struct bt_error_cause base; + GString *comp_name; + struct bt_error_cause_component_class_id comp_class_id; +}; + +struct bt_error_cause_component_class_actor { + struct bt_error_cause base; + struct bt_error_cause_component_class_id comp_class_id; +}; + +struct bt_error_cause_message_iterator_actor { + struct bt_error_cause base; + GString *comp_name; + GString *output_port_name; + struct bt_error_cause_component_class_id comp_class_id; +}; + +struct bt_error { + /* + * Array of `struct bt_error_cause *` (or an extension); owned + * by this. + */ + GPtrArray *causes; +}; + +static inline +const char *bt_error_cause_actor_type_string( + enum bt_error_cause_actor_type actor_type) +{ + switch (actor_type) { + case BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN: + return "BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN"; + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT: + return "BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT"; + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS: + return "BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS"; + case BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR: + return "BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR"; + default: + return "(unknown)"; + } +}; + +BT_HIDDEN +struct bt_error *bt_error_create(void); + +BT_HIDDEN +void bt_error_destroy(struct bt_error *error); + +BT_HIDDEN +int bt_error_append_cause_from_unknown(struct bt_error *error, + const char *module_name, const char *file_name, + uint64_t line_no, const char *msg_fmt, va_list args); + +BT_HIDDEN +int bt_error_append_cause_from_component( + struct bt_error *error, bt_self_component *self_comp, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args); + +BT_HIDDEN +int bt_error_append_cause_from_component_class( + struct bt_error *error, + bt_self_component_class *self_comp_class, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args); + +BT_HIDDEN +int bt_error_append_cause_from_message_iterator( + struct bt_error *error, bt_self_message_iterator *self_iter, + const char *file_name, uint64_t line_no, + const char *msg_fmt, va_list args); + +#endif /* BABELTRACE_ERROR_INTERNAL_H */ diff --git a/src/lib/lib-logging.c b/src/lib/lib-logging.c index 93a2c989..1c5338c5 100644 --- a/src/lib/lib-logging.c +++ b/src/lib/lib-logging.c @@ -76,6 +76,8 @@ #include "trace-ir/trace-class.h" #include "trace-ir/trace.h" #include "trace-ir/utils.h" +#include "error.h" +#include "assert-pre.h" #define LIB_LOGGING_BUF_SIZE (4096 * 4) @@ -1281,6 +1283,66 @@ static inline void format_plugin(char **buf_ch, bool extended, } } +static inline void format_error_cause(char **buf_ch, bool extended, + const char *prefix, const struct bt_error_cause *cause) +{ + const struct bt_error_cause_component_class_id *comp_class_id = NULL; + + BUF_APPEND(", %sactor-type=%s, %smodule-name=\"%s\"", + PRFIELD(bt_error_cause_actor_type_string(cause->actor_type)), + PRFIELD_GSTRING(cause->module_name)); + + if (!extended) { + return; + } + + BUF_APPEND(", %spartial-msg=\"%.32s\"", + PRFIELD_GSTRING(cause->message)); + + switch (cause->actor_type) { + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT: + { + const struct bt_error_cause_component_actor *spec_cause = + (const void *) cause; + + BUF_APPEND(", %scomp-name=\"%s\"", + PRFIELD_GSTRING(spec_cause->comp_name)); + comp_class_id = &spec_cause->comp_class_id; + break; + } + case BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS: + { + const struct bt_error_cause_component_class_actor *spec_cause = + (const void *) cause; + + comp_class_id = &spec_cause->comp_class_id; + break; + } + case BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR: + { + const struct bt_error_cause_message_iterator_actor *spec_cause = + (const void *) cause; + + BUF_APPEND(", %scomp-name=\"%s\", %scomp-out-port-name=\"%s\"", + PRFIELD_GSTRING(spec_cause->comp_name), + PRFIELD_GSTRING(spec_cause->output_port_name)); + comp_class_id = &spec_cause->comp_class_id; + break; + } + default: + break; + } + + if (comp_class_id) { + BUF_APPEND(", %scomp-cls-type=%s, %scomp-cls-name=\"%s\", " + "%splugin-name=\"%s\"", + PRFIELD(bt_component_class_type_string( + comp_class_id->type)), + PRFIELD_GSTRING(comp_class_id->name), + PRFIELD_GSTRING(comp_class_id->plugin_name)); + } +} + static inline void handle_conversion_specifier_bt(void *priv_data, char **buf_ch, size_t avail_size, const char **out_fmt_ch, va_list *args) @@ -1402,6 +1464,9 @@ static inline void handle_conversion_specifier_bt(void *priv_data, case 'O': format_object(buf_ch, extended, prefix, obj); break; + case 'r': + format_error_cause(buf_ch, extended, prefix, obj); + break; default: abort(); }