lib: add thread-safe error reporting API
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 15 Jun 2019 20:48:07 +0000 (16:48 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 6 Jul 2019 03:47:50 +0000 (23:47 -0400)
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 <eeppeliteloop@gmail.com>
Change-Id: Ia791b2ff9bbf7a91e7c37416ba748c1186ee69a4
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1474
Tested-by: jenkins <jenkins@lttng.org>
12 files changed:
CONTRIBUTING.adoc
include/Makefile.am
include/babeltrace2/babeltrace.h
include/babeltrace2/current-thread.h [new file with mode: 0644]
include/babeltrace2/error-cause-const.h [new file with mode: 0644]
include/babeltrace2/error-const.h [new file with mode: 0644]
include/babeltrace2/types.h
src/lib/Makefile.am
src/lib/current-thread.c [new file with mode: 0644]
src/lib/error.c [new file with mode: 0644]
src/lib/error.h [new file with mode: 0644]
src/lib/lib-logging.c

index 62dd8a9d890d0b2e8685bebedd62e1881cc4d88a..df4ae3cf167558a04d9b9d04952ac47a489f86b5 100644 (file)
@@ -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 *+`
index 320846611805417d2d93f96e4007c05b60e69365..4507108363d10238573902fa623370292e393656 100644 (file)
@@ -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 \
index 646cff67b6b077dcd75bf29a25c1a07b5056527f..290b47a0fbf9380857d6200701dffa583b7edd44 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 /* Core API */
+#include <babeltrace2/current-thread.h>
+#include <babeltrace2/error-cause-const.h>
+#include <babeltrace2/error-const.h>
 #include <babeltrace2/logging.h>
 #include <babeltrace2/property.h>
 #include <babeltrace2/types.h>
diff --git a/include/babeltrace2/current-thread.h b/include/babeltrace2/current-thread.h
new file mode 100644 (file)
index 0000000..e89d052
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef BABELTRACE_CURRENT_THREAD_H
+#define BABELTRACE_CURRENT_THREAD_H
+
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdarg.h>
+
+/*
+ * For bt_error, bt_self_component, bt_self_component_class, and
+ * bt_self_message_iterator
+ */
+#include <babeltrace2/types.h>
+
+/* For __BT_FUNC_STATUS_* */
+#define __BT_FUNC_STATUS_ENABLE
+#include <babeltrace2/func-status.h>
+#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 <babeltrace2/undef-func-status.h>
+
+#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 (file)
index 0000000..472d537
--- /dev/null
@@ -0,0 +1,116 @@
+#ifndef BABELTRACE_ERROR_CAUSE_CONST_H
+#define BABELTRACE_ERROR_CAUSE_CONST_H
+
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+
+/* For bt_error, bt_error_cause */
+#include <babeltrace2/types.h>
+
+/* For bt_component_class_type */
+#include <babeltrace2/graph/component-class-const.h>
+
+#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 (file)
index 0000000..0aaddb5
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef BABELTRACE_ERROR_CONST_H
+#define BABELTRACE_ERROR_CONST_H
+
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+
+/* For bt_error, bt_error_cause */
+#include <babeltrace2/types.h>
+
+#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 */
index 6884f175103a17d159a626ff84a0f84c5d57e437..86f6501a89b5d8c000836e1c3da834597c5434b8 100644 (file)
@@ -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;
index c00a0ee16fd0de6a98c5221b78226d817928c9c4..319f0fccf506d329523955eee8af44ae05805f44 100644 (file)
@@ -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 (file)
index 0000000..959e169
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace2/current-thread.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#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 (file)
index 0000000..b19fe1d
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <babeltrace2/error-const.h>
+#include <babeltrace2/error-cause-const.h>
+
+#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 (file)
index 0000000..e23494d
--- /dev/null
@@ -0,0 +1,121 @@
+#ifndef BABELTRACE_ERROR_INTERNAL_H
+#define BABELTRACE_ERROR_INTERNAL_H
+
+/*
+ * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdarg.h>
+#include <glib.h>
+#include <babeltrace2/error-const.h>
+#include <babeltrace2/error-cause-const.h>
+#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 */
index 93a2c989f5aa0adf7b9d911741e127bedc214b92..1c5338c55ecb9ea953f195d0ccf4ed578b9d9f89 100644 (file)
@@ -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();
        }
This page took 0.042652 seconds and 4 git commands to generate.