lib: bt_plugin_find*(): return status code; add "fail on load error" param
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 25 Jun 2019 16:50:17 +0000 (12:50 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 26 Jun 2019 16:31:23 +0000 (12:31 -0400)
The bt_plugin_find*() functions, before this patch, return a plugin
object or a plugin set object. When such a function returns `NULL`, you
have no way to tell if it's because there was an error or if no plugins
were found.

Also, when a plugin cannot be loaded for various reasons, sometimes it's
considered an error and sometimes not, so there's no way to be strict.

This patch makes:

* The bt_plugin_find*() functions return a status code, and return the
  plugin or plugin set object by output parameter.

  The available, new status codes are:

  `BT_PLUGIN_STATUS_OK`:
        Everything is fine.

  `BT_PLUGIN_STATUS_NOT_FOUND`:
        No plugins were found.

  `BT_PLUGIN_STATUS_ERROR`:
        There was a general error while trying to find plugins.

  `BT_PLUGIN_STATUS_LOADING_ERROR`:
        A plugin could not be loaded successfully while trying to find
        plugins.

  `BT_PLUGIN_STATUS_NOMEM`:
        There was an allocation error while trying to find plugins.

  The bt_plugin_find_all_from_file() and bt_plugin_find_all_from_dir()
  never return an empty plugin set: they return
  `BT_PLUGIN_STATUS_NOT_FOUND` instead.

* The bt_plugin_find*() functions accept a new `fail_on_load_error`
  parameter which controls whether a plugin loading error is ignored
  (can eventually lead to `BT_PLUGIN_STATUS_NOT_FOUND` being returned)
  or triggers a loading error condition
  (`BT_PLUGIN_STATUS_LOADING_ERROR`).

When `fail_on_load_error` is true, the functions log errors with
the WARN level. When it's false, they log errors with the INFO level
before ignoring them.

A file is known to be a Babeltrace plugin if:

* Its extension is `.so` (or `.dll` on Windows), g_module_open()
  succeeds for that file, and the required
  `__bt_get_begin_section_plugin_descriptors` symbol can be found.

* Its name starts with `bt_` and ends with `.py`.

Any file that does not meet the criteria above does not trigger a
loading error: the file is simply skipped.

A plugin loading error (`BT_PLUGIN_STATUS_LOADING_ERROR`) is caused by:

For a shared object (`.so` or `.dll`) plugin, one of:
    * Missing `__bt_get_end_section_plugin_descriptors` symbol.
    * Missing `__bt_get_begin_section_plugin_descriptor_attributes` and
      `__bt_get_end_section_plugin_descriptor_attributes` symbols.
    * Missing `__bt_get_begin_section_component_class_descriptors` and
      `__bt_get_end_section_component_class_descriptors` symbols.
    * Missing
      `__bt_get_begin_section_component_class_descriptor_attributes` and
      `__bt_get_end_section_component_class_descriptor_attributes`
      symbols.
    * Unknown plugin descriptor attribute.
    * Unknown component class descriptor attribute.
    * Unknown component class type in component class descriptors.
    * Incompatible or missing ABI version.
    * Failing plugin's user initialization function.

For a built-in plugin:
    * Failing plugin's user initialization function.

For a Python plugin, one of:
    * Failure to load the Python plugin module.
    * Unexpected/malformed plugin info object (missing attributes, wrong
      types, etc.).

With this patch, you must pass a valid file or directory path to
bt_plugin_find_all_from_file() and bt_plugin_find_all_from_dir(). When
you don't, the functions fail with `BT_PLUGIN_STATUS_ERROR`. This is not
considered a loading error (not `BT_PLUGIN_STATUS_LOADING_ERROR`), as
the user can control whether or not this error occurs­ by making sure
the path exists, and it does not occur while loading a plugin.

As of this patch, the CLI ignores loading errors (like before), that is,
passes `BT_FALSE` as the `fail_on_load_error` parameter. A strict mode
could be added, controlled by a command-line option.

In `native_bt_plugin.i`, wrappers are created to set the output
parameter to `NULL` if the status is not `BT_PLUGIN_STATUS_OK`. This is
similar to what was already done to wrap bt_plugin_get_version().

If you pass `fail_on_load_error=True` to bt2.find_plugins() or
bt2.find_plugin(), then this function can raise `bt2.Error` if there's
a loading error.

Tests are adapted to check the returned status code.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I88049c07163055503e9551940973390ebedcfae4
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1537
Reviewed-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Tested-by: jenkins <jenkins@lttng.org>
13 files changed:
include/babeltrace2/plugin/plugin-const.h
src/bindings/python/bt2/bt2/__init__.py.in
src/bindings/python/bt2/bt2/native_bt_plugin.i
src/bindings/python/bt2/bt2/plugin.py
src/cli/babeltrace2.c
src/lib/plugin/plugin-so.c
src/lib/plugin/plugin-so.h
src/lib/plugin/plugin.c
src/lib/plugin/plugin.h
src/python-plugin-provider/python-plugin-provider.c
src/python-plugin-provider/python-plugin-provider.h
tests/bindings/python/bt2/test_plugin.py
tests/lib/test_plugin.c

index e9d3ee347a5479df94a8b2b25b062b8a46944216..e8a8a2b640a28f6713e85fecaa1c732f422b71c2 100644 (file)
 extern "C" {
 #endif
 
-extern const bt_plugin *bt_plugin_find(const char *plugin_name);
-
-extern const bt_plugin_set *bt_plugin_find_all_from_file(
-               const char *path);
-
-extern const bt_plugin_set *bt_plugin_find_all_from_dir(
-               const char *path, bt_bool recurse);
-
-extern const bt_plugin_set *bt_plugin_find_all_from_static(void);
+typedef enum bt_plugin_status {
+       BT_PLUGIN_STATUS_OK = 0,
+       BT_PLUGIN_STATUS_NOT_FOUND = 2,
+       BT_PLUGIN_STATUS_ERROR = -1,
+       BT_PLUGIN_STATUS_LOADING_ERROR = -2,
+       BT_PLUGIN_STATUS_NOMEM = -12,
+} bt_plugin_status;
+
+extern bt_plugin_status bt_plugin_find(const char *plugin_name,
+               bt_bool fail_on_load_error, const bt_plugin **plugin);
+
+extern bt_plugin_status bt_plugin_find_all_from_file(
+               const char *path, bt_bool fail_on_load_error,
+               const bt_plugin_set **plugin_set);
+
+extern bt_plugin_status bt_plugin_find_all_from_dir(
+               const char *path, bt_bool recurse, bt_bool fail_on_load_error,
+               const bt_plugin_set **plugin_set);
+
+extern bt_plugin_status bt_plugin_find_all_from_static(
+               bt_bool fail_on_load_error,
+               const bt_plugin_set **plugin_set);
 
 extern const char *bt_plugin_get_name(const bt_plugin *plugin);
 
index fe987a3dc9e58afe10e9281ea7ee38a5dd80a664..98a1eb5d4e214718bc6fb52cfccbf4e979804d4b 100644 (file)
@@ -106,6 +106,10 @@ class NonexistentClockSnapshot(Error):
     pass
 
 
+class PluginLoadingError(Error):
+    pass
+
+
 class _ListenerHandle:
     def __init__(self, listener_id, obj):
         self._listener_id = listener_id
index 435d40f2ed7de5b86243d5e26d18bf6710d72baa..44633f2c4a0311e97ffa74a6a52a2a4340558a87 100644 (file)
  * THE SOFTWARE.
  */
 
-/* From plugin-const.h */
+/* Output argument typemap for plugin output (always appends) */
+%typemap(in, numinputs=0)
+       (const bt_plugin **OUT)
+       (bt_plugin *temp_plugin = NULL) {
+       $1 = &temp_plugin;
+}
 
-extern const bt_plugin *bt_plugin_find(const char *plugin_name);
+%typemap(argout)
+       (const bt_plugin **OUT) {
+       if (*$1) {
+               /* SWIG_Python_AppendOutput() steals the created object */
+               $result = SWIG_Python_AppendOutput($result,
+                               SWIG_NewPointerObj(SWIG_as_voidptr(*$1),
+                                       SWIGTYPE_p_bt_plugin, 0));
+       } else {
+               /* SWIG_Python_AppendOutput() steals Py_None */
+               Py_INCREF(Py_None);
+               $result = SWIG_Python_AppendOutput($result, Py_None);
+       }
+}
 
-extern const bt_plugin_set *bt_plugin_find_all_from_file(
-               const char *path);
+/* Output argument typemap for plugin set output (always appends) */
+%typemap(in, numinputs=0)
+       (const bt_plugin_set **OUT)
+       (bt_plugin_set *temp_plugin_set = NULL) {
+       $1 = &temp_plugin_set;
+}
 
-extern const bt_plugin_set *bt_plugin_find_all_from_dir(
-               const char *path, bt_bool recurse);
+%typemap(argout)
+       (const bt_plugin_set **OUT) {
+       if (*$1) {
+               /* SWIG_Python_AppendOutput() steals the created object */
+               $result = SWIG_Python_AppendOutput($result,
+                               SWIG_NewPointerObj(SWIG_as_voidptr(*$1),
+                                       SWIGTYPE_p_bt_plugin_set, 0));
+       } else {
+               /* SWIG_Python_AppendOutput() steals Py_None */
+               Py_INCREF(Py_None);
+               $result = SWIG_Python_AppendOutput($result, Py_None);
+       }
+}
+
+/* From plugin-const.h */
 
-extern const bt_plugin_set *bt_plugin_find_all_from_static(void);
+typedef enum bt_plugin_status {
+       BT_PLUGIN_STATUS_OK = 0,
+       BT_PLUGIN_STATUS_NOT_FOUND = 2,
+       BT_PLUGIN_STATUS_ERROR = -1,
+       BT_PLUGIN_STATUS_LOADING_ERROR = -2,
+       BT_PLUGIN_STATUS_NOMEM = -12,
+} bt_plugin_status;
 
 extern const char *bt_plugin_get_name(const bt_plugin *plugin);
 
@@ -44,10 +84,6 @@ extern const char *bt_plugin_get_description(const bt_plugin *plugin);
 
 extern const char *bt_plugin_get_path(const bt_plugin *plugin);
 
-extern bt_property_availability bt_plugin_get_version(
-               const bt_plugin *plugin, unsigned int *OUT,
-               unsigned int *OUT, unsigned int *OUT, const char **OUT);
-
 extern uint64_t bt_plugin_get_source_component_class_count(
                const bt_plugin *plugin);
 
@@ -103,14 +139,25 @@ bt_property_availability bt_plugin_get_version_wrapper(
                const bt_plugin *plugin, unsigned int *OUT,
                unsigned int *OUT, unsigned int *OUT, const char **OUT);
 
+bt_plugin_status bt_plugin_find_wrapper(const char *plugin_name,
+               bt_bool fail_on_load_error, const bt_plugin **OUT);
+
+bt_plugin_status bt_plugin_find_all_from_file_wrapper(
+               const char *path, bt_bool fail_on_load_error,
+               const bt_plugin_set **OUT);
+
+bt_plugin_status bt_plugin_find_all_from_dir_wrapper(
+               const char *path, bt_bool recurse, bt_bool fail_on_load_error,
+               const bt_plugin_set **OUT);
+
 %{
 
 /*
- * This wrapper ensures that when the API function fails, the `*extra` output
- * parameter is set to NULL.  This is necessary because the epilogue of the
- * "char **OUT" typemap will use that value to make a Python str object.  We
- * can't rely on the initial value of `*extra`, it could point to unreadable
- * memory.
+ * This *_wrapper() functions below ensure that when the API function
+ * fails, the output parameter is set to `NULL`.  This is necessary
+ * because the epilogue of the `something **OUT` typemap will use that
+ * value to make a Python object.  We can't rely on the initial value of
+ * `*OUT`; it could point to unreadable memory.
  */
 
 bt_property_availability bt_plugin_get_version_wrapper(
@@ -128,4 +175,48 @@ bt_property_availability bt_plugin_get_version_wrapper(
        return ret;
 }
 
+bt_plugin_status bt_plugin_find_wrapper(const char *plugin_name,
+               bt_bool fail_on_load_error, const bt_plugin **plugin)
+{
+       bt_plugin_status status;
+
+       status = bt_plugin_find(plugin_name, fail_on_load_error,
+               plugin);
+       if (status != BT_PLUGIN_STATUS_OK) {
+               *plugin = NULL;
+       }
+
+       return status;
+}
+
+bt_plugin_status bt_plugin_find_all_from_file_wrapper(
+               const char *path, bt_bool fail_on_load_error,
+               const bt_plugin_set **plugin_set)
+{
+       bt_plugin_status status;
+
+       status = bt_plugin_find_all_from_file(path, fail_on_load_error,
+               plugin_set);
+       if (status != BT_PLUGIN_STATUS_OK) {
+               *plugin_set = NULL;
+       }
+
+       return status;
+}
+
+bt_plugin_status bt_plugin_find_all_from_dir_wrapper(
+               const char *path, bt_bool recurse, bt_bool fail_on_load_error,
+               const bt_plugin_set **plugin_set)
+{
+       bt_plugin_status status;
+
+       status = bt_plugin_find_all_from_dir(path, recurse, fail_on_load_error,
+               plugin_set);
+       if (status != BT_PLUGIN_STATUS_OK) {
+               *plugin_set = NULL;
+       }
+
+       return status;
+}
+
 %}
index 2d0d8b057303afa44d1e8cb0a832ac429c4b11c3..b32f8ce236b50853116a738e8e44d55f96f65936 100644 (file)
@@ -27,29 +27,45 @@ import os.path
 import bt2
 
 
-def find_plugins(path, recurse=True):
+def _handle_status(status, gen_error_msg):
+    if status == native_bt.PLUGIN_STATUS_LOADING_ERROR:
+        raise bt2.PluginLoadingError
+    elif status < 0:
+        raise bt2.Error(gen_error_msg)
+
+
+def find_plugins(path, recurse=True, fail_on_load_error=False):
     utils._check_str(path)
     utils._check_bool(recurse)
+    utils._check_bool(fail_on_load_error)
     plugin_set_ptr = None
 
     if os.path.isfile(path):
-        plugin_set_ptr = native_bt.plugin_find_all_from_file(path)
+        status, plugin_set_ptr = native_bt.plugin_find_all_from_file_wrapper(path, fail_on_load_error)
     elif os.path.isdir(path):
-        plugin_set_ptr = native_bt.plugin_find_all_from_dir(path, int(recurse))
+        status, plugin_set_ptr = native_bt.plugin_find_all_from_dir_wrapper(path, int(recurse), int(fail_on_load_error))
+    else:
+        raise bt2.Error("invalid path: '{}'".format(path))
+
+    _handle_status(status, 'failed to find plugins')
 
-    if plugin_set_ptr is None:
+    if status == native_bt.PLUGIN_STATUS_NOT_FOUND:
         return
 
+    assert plugin_set_ptr is not None
     return _PluginSet._create_from_ptr(plugin_set_ptr)
 
 
-def find_plugin(name):
+def find_plugin(name, fail_on_load_error=False):
     utils._check_str(name)
-    ptr = native_bt.plugin_find(name)
+    utils._check_bool(fail_on_load_error)
+    status, ptr = native_bt.plugin_find_wrapper(name, int(fail_on_load_error))
+    _handle_status(status, 'failed to find plugin')
 
-    if ptr is None:
+    if status == native_bt.PLUGIN_STATUS_NOT_FOUND:
         return
 
+    assert ptr is not None
     return _Plugin._create_from_ptr(ptr)
 
 
index 3cb751c3c5770af3d71c897df62e42c6af91a888..c89fabce97ad9e3dd5e43f979517c1fad8d38bd8 100644 (file)
@@ -797,7 +797,8 @@ int load_dynamic_plugins(const bt_value *plugin_paths)
        for (i = 0; i < nr_paths; i++) {
                const bt_value *plugin_path_value = NULL;
                const char *plugin_path;
-               const bt_plugin_set *plugin_set;
+               const bt_plugin_set *plugin_set = NULL;
+               bt_plugin_status status;
 
                plugin_path_value =
                        bt_value_array_borrow_element_by_index_const(
@@ -815,13 +816,20 @@ int load_dynamic_plugins(const bt_value *plugin_paths)
                        continue;
                }
 
-               plugin_set = bt_plugin_find_all_from_dir(plugin_path, false);
-               if (!plugin_set) {
-                       BT_LOGI("Unable to load dynamic plugins from directory: "
+               status = bt_plugin_find_all_from_dir(plugin_path, BT_FALSE,
+                       BT_FALSE, &plugin_set);
+               if (status < 0) {
+                       BT_LOGE("Unable to load dynamic plugins from directory: "
                                "path=\"%s\"", plugin_path);
                        continue;
+               } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+                       BT_LOGI("No plugins found in directory: path=\"%s\"",
+                               plugin_path);
+                       continue;
                }
 
+               BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+               BT_ASSERT(plugin_set);
                add_to_loaded_plugins(plugin_set);
                bt_plugin_set_put_ref(plugin_set);
        }
@@ -834,15 +842,21 @@ int load_static_plugins(void)
 {
        int ret = 0;
        const bt_plugin_set *plugin_set;
+       bt_plugin_status status;
 
        BT_LOGI("Loading static plugins.");
-       plugin_set = bt_plugin_find_all_from_static();
-       if (!plugin_set) {
+       status = bt_plugin_find_all_from_static(BT_FALSE, &plugin_set);
+       if (status < 0) {
                BT_LOGE("Unable to load static plugins.");
                ret = -1;
                goto end;
+       } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+               BT_LOGI("No static plugins found.");
+               goto end;
        }
 
+       BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+       BT_ASSERT(plugin_set);
        add_to_loaded_plugins(plugin_set);
        bt_plugin_set_put_ref(plugin_set);
 end:
index 2ecb1a7e3bee22a3de0a2af69f37f2099f587356..42b1df6ce4c8de1bb157d6f002364867d8e61725 100644 (file)
@@ -174,33 +174,37 @@ void bt_plugin_so_shared_lib_handle_destroy(struct bt_object *obj)
 }
 
 static
-struct bt_plugin_so_shared_lib_handle *bt_plugin_so_shared_lib_handle_create(
-               const char *path)
+enum bt_plugin_status bt_plugin_so_shared_lib_handle_create(
+               const char *path,
+               struct bt_plugin_so_shared_lib_handle **shared_lib_handle)
 {
-       struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL;
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
 
+       BT_ASSERT(shared_lib_handle);
        BT_LOGI("Creating shared library handle: path=\"%s\"", path);
-       shared_lib_handle = g_new0(struct bt_plugin_so_shared_lib_handle, 1);
-       if (!shared_lib_handle) {
+       *shared_lib_handle = g_new0(struct bt_plugin_so_shared_lib_handle, 1);
+       if (!*shared_lib_handle) {
                BT_LOGE_STR("Failed to allocate one shared library handle.");
-               goto error;
+               status = BT_PLUGIN_STATUS_NOMEM;
+               goto end;
        }
 
-       bt_object_init_shared(&shared_lib_handle->base,
+       bt_object_init_shared(&(*shared_lib_handle)->base,
                bt_plugin_so_shared_lib_handle_destroy);
 
        if (!path) {
                goto end;
        }
 
-       shared_lib_handle->path = g_string_new(path);
-       if (!shared_lib_handle->path) {
+       (*shared_lib_handle)->path = g_string_new(path);
+       if (!(*shared_lib_handle)->path) {
                BT_LOGE_STR("Failed to allocate a GString.");
-               goto error;
+               status = BT_PLUGIN_STATUS_NOMEM;
+               goto end;
        }
 
-       shared_lib_handle->module = g_module_open(path, G_MODULE_BIND_LOCAL);
-       if (!shared_lib_handle->module) {
+       (*shared_lib_handle)->module = g_module_open(path, G_MODULE_BIND_LOCAL);
+       if (!(*shared_lib_handle)->module) {
                /*
                 * INFO-level logging because we're only _trying_ to
                 * open this file as a Babeltrace plugin: if it's not,
@@ -210,21 +214,21 @@ struct bt_plugin_so_shared_lib_handle *bt_plugin_so_shared_lib_handle_create(
                 */
                BT_LOGI("Cannot open GModule: %s: path=\"%s\"",
                        g_module_error(), path);
-               goto error;
+               BT_OBJECT_PUT_REF_AND_RESET(*shared_lib_handle);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
+               goto end;
        }
 
        goto end;
 
-error:
-       BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle);
-
 end:
-       if (shared_lib_handle) {
+       BT_ASSERT(*shared_lib_handle || status != BT_PLUGIN_STATUS_OK);
+       if (*shared_lib_handle) {
                BT_LOGI("Created shared library handle: path=\"%s\", addr=%p",
-                       path, shared_lib_handle);
+                       path, *shared_lib_handle);
        }
 
-       return shared_lib_handle;
+       return status;
 }
 
 static
@@ -271,8 +275,8 @@ void bt_plugin_so_destroy_spec_data(struct bt_plugin *plugin)
  * 6. Freeze the plugin object.
  */
 static
-enum bt_plugin_status bt_plugin_so_init(
-               struct bt_plugin *plugin,
+enum bt_plugin_status bt_plugin_so_init(struct bt_plugin *plugin,
+               bool fail_on_load_error,
                const struct __bt_plugin_descriptor *descriptor,
                struct __bt_plugin_descriptor_attribute const * const *attrs_begin,
                struct __bt_plugin_descriptor_attribute const * const *attrs_end,
@@ -352,7 +356,7 @@ enum bt_plugin_status bt_plugin_so_init(
                sizeof(struct comp_class_full_descriptor));
        if (!comp_class_full_descriptors) {
                BT_LOGE_STR("Failed to allocate a GArray.");
-               status = BT_PLUGIN_STATUS_ERROR;
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto end;
        }
 
@@ -400,21 +404,24 @@ enum bt_plugin_status bt_plugin_so_init(
                                cur_attr->value.version.extra);
                        break;
                default:
-                       /*
-                        * WARN-level logging because this should not
-                        * happen with the appropriate ABI version. If
-                        * we're here, we know that for the reported
-                        * version of the ABI, this attribute is
-                        * unknown.
-                        */
-                       BT_LOGW("Ignoring unknown plugin descriptor attribute: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "%s plugin descriptor attribute: "
                                "plugin-path=\"%s\", plugin-name=\"%s\", "
                                "attr-type-name=\"%s\", attr-type-id=%d",
+                               fail_on_load_error ? "Unknown" :
+                                       "Ignoring unknown",
                                spec->shared_lib_handle->path ?
                                        spec->shared_lib_handle->path->str :
                                        NULL,
                                descriptor->name, cur_attr->type_name,
                                cur_attr->type);
+
+                       if (fail_on_load_error) {
+                               status = BT_PLUGIN_STATUS_LOADING_ERROR;
+                               goto end;
+                       }
+
                        break;
                }
        }
@@ -660,21 +667,17 @@ enum bt_plugin_status bt_plugin_so_init(
                                }
                                break;
                        default:
-                               /*
-                                * WARN-level logging because this
-                                * should not happen with the
-                                * appropriate ABI version. If we're
-                                * here, we know that for the reported
-                                * version of the ABI, this attribute is
-                                * unknown.
-                                */
-                               BT_LOGW("Ignoring unknown component class descriptor attribute: "
+                               BT_LOG_WRITE(fail_on_load_error ?
+                                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                                       "%s component class descriptor attribute: "
                                        "plugin-path=\"%s\", "
                                        "plugin-name=\"%s\", "
                                        "comp-class-name=\"%s\", "
                                        "comp-class-type=%s, "
                                        "attr-type-name=\"%s\", "
                                        "attr-type-id=%d",
+                                       fail_on_load_error ? "Unknown" :
+                                               "Ignoring unknown",
                                        spec->shared_lib_handle->path ?
                                                spec->shared_lib_handle->path->str :
                                                NULL,
@@ -684,6 +687,12 @@ enum bt_plugin_status bt_plugin_so_init(
                                                cur_cc_descr_attr->comp_class_descriptor->type),
                                        cur_cc_descr_attr->type_name,
                                        cur_cc_descr_attr->type);
+
+                               if (fail_on_load_error) {
+                                       status = BT_PLUGIN_STATUS_LOADING_ERROR;
+                                       goto end;
+                               }
+
                                break;
                        }
                }
@@ -695,13 +704,20 @@ enum bt_plugin_status bt_plugin_so_init(
 
                BT_LOGD_STR("Calling user's plugin initialization function.");
                init_status = spec->init((void *) plugin);
-               BT_LOGD("User function returned: %s",
+               BT_LOGD("User function returned: status=%s",
                        bt_self_plugin_status_string(init_status));
 
                if (init_status < 0) {
-                       BT_LOGW_STR("User's plugin initialization function failed.");
-                       status = BT_PLUGIN_STATUS_ERROR;
-                       goto end;
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "User's plugin initialization function failed: "
+                               "status=%s",
+                               bt_self_plugin_status_string(init_status));
+
+                       if (fail_on_load_error) {
+                               status = (int) init_status;
+                               goto end;
+                       }
                }
        }
 
@@ -751,28 +767,31 @@ enum bt_plugin_status bt_plugin_so_init(
                                sink_comp_class);
                        break;
                default:
-                       /*
-                        * WARN-level logging because this should not
-                        * happen with the appropriate ABI version. If
-                        * we're here, we know that for the reported
-                        * version of the ABI, this component class type
-                        * is unknown.
-                        */
-                       BT_LOGW("Ignoring unknown component class type: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "%s component class type: "
                                "plugin-path=\"%s\", plugin-name=\"%s\", "
                                "comp-class-name=\"%s\", comp-class-type=%d",
+                               fail_on_load_error ? "Unknown" :
+                                       "Ignoring unknown",
                                spec->shared_lib_handle->path->str ?
                                        spec->shared_lib_handle->path->str :
                                        NULL,
                                descriptor->name,
                                cc_full_descr->descriptor->name,
                                cc_full_descr->descriptor->type);
+
+                       if (fail_on_load_error) {
+                               status = BT_PLUGIN_STATUS_LOADING_ERROR;
+                               goto end;
+                       }
+
                        continue;
                }
 
                if (!comp_class) {
                        BT_LOGE_STR("Cannot create component class.");
-                       status = BT_PLUGIN_STATUS_ERROR;
+                       status = BT_PLUGIN_STATUS_NOMEM;
                        goto end;
                }
 
@@ -781,7 +800,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                comp_class, cc_full_descr->description);
                        if (ret) {
                                BT_LOGE_STR("Cannot set component class's description.");
-                               status = BT_PLUGIN_STATUS_ERROR;
+                               status = BT_PLUGIN_STATUS_NOMEM;
                                BT_OBJECT_PUT_REF_AND_RESET(comp_class);
                                goto end;
                        }
@@ -792,7 +811,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                cc_full_descr->help);
                        if (ret) {
                                BT_LOGE_STR("Cannot set component class's help string.");
-                               status = BT_PLUGIN_STATUS_ERROR;
+                               status = BT_PLUGIN_STATUS_NOMEM;
                                BT_OBJECT_PUT_REF_AND_RESET(comp_class);
                                goto end;
                        }
@@ -806,7 +825,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.init);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's initialization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -818,7 +837,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.finalize);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's finalization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -830,7 +849,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.query);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's query method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -842,7 +861,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.output_port_connected);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's \"output port connected\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -854,7 +873,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_init);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator initialization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -866,7 +885,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_finalize);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator finalization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -878,7 +897,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_seek_ns_from_origin);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator \"seek nanoseconds from origin\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -890,7 +909,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_seek_beginning);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator \"seek beginning\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -902,7 +921,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator \"can seek nanoseconds from origin\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -914,7 +933,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.source.msg_iter_can_seek_beginning);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set source component class's message iterator \"can seek beginning\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(src_comp_class);
                                        goto end;
                                }
@@ -928,7 +947,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.init);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's initialization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -940,7 +959,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.finalize);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's finalization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -952,7 +971,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.query);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's query method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -964,7 +983,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.input_port_connected);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's \"input port connected\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -976,7 +995,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.output_port_connected);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's \"output port connected\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -988,7 +1007,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_init);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator initialization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1000,7 +1019,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_finalize);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator finalization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1012,7 +1031,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator \"seek nanoseconds from origin\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1024,7 +1043,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_seek_beginning);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator \"seek beginning\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1036,7 +1055,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek nanoseconds from origin\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1048,7 +1067,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.filter.msg_iter_can_seek_beginning);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek beginning\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class);
                                        goto end;
                                }
@@ -1062,7 +1081,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.sink.init);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set sink component class's initialization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class);
                                        goto end;
                                }
@@ -1074,7 +1093,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.sink.finalize);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set sink component class's finalization method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class);
                                        goto end;
                                }
@@ -1086,7 +1105,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.sink.query);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set sink component class's query method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class);
                                        goto end;
                                }
@@ -1098,7 +1117,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.sink.input_port_connected);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set sink component class's \"input port connected\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class);
                                        goto end;
                                }
@@ -1110,7 +1129,7 @@ enum bt_plugin_status bt_plugin_so_init(
                                        cc_full_descr->methods.sink.graph_is_configured);
                                if (ret) {
                                        BT_LOGE_STR("Cannot set sink component class's \"graph is configured\" method.");
-                                       status = BT_PLUGIN_STATUS_ERROR;
+                                       status = BT_PLUGIN_STATUS_NOMEM;
                                        BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class);
                                        goto end;
                                }
@@ -1192,8 +1211,9 @@ size_t count_non_null_items_in_section(const void *begin, const void *end)
 }
 
 static
-struct bt_plugin_set *bt_plugin_so_create_all_from_sections(
+enum bt_plugin_status bt_plugin_so_create_all_from_sections(
                struct bt_plugin_so_shared_lib_handle *shared_lib_handle,
+               bool fail_on_load_error,
                struct __bt_plugin_descriptor const * const *descriptors_begin,
                struct __bt_plugin_descriptor const * const *descriptors_end,
                struct __bt_plugin_descriptor_attribute const * const *attrs_begin,
@@ -1201,20 +1221,22 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_sections(
                struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin,
                struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end,
                struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin,
-               struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end)
+               struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end,
+               struct bt_plugin_set **plugin_set_out)
 {
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
        size_t descriptor_count;
        size_t attrs_count;
        size_t cc_descriptors_count;
        size_t cc_descr_attrs_count;
        size_t i;
-       struct bt_plugin_set *plugin_set = NULL;
 
+       BT_ASSERT(plugin_set_out);
+       *plugin_set_out = NULL;
        descriptor_count = count_non_null_items_in_section(descriptors_begin, descriptors_end);
        attrs_count = count_non_null_items_in_section(attrs_begin, attrs_end);
        cc_descriptors_count = count_non_null_items_in_section(cc_descriptors_begin, cc_descriptors_end);
        cc_descr_attrs_count =  count_non_null_items_in_section(cc_descr_attrs_begin, cc_descr_attrs_end);
-
        BT_LOGI("Creating all SO plugins from sections: "
                "plugin-path=\"%s\", "
                "descr-begin-addr=%p, descr-end-addr=%p, "
@@ -1230,9 +1252,10 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_sections(
                cc_descr_attrs_begin, cc_descr_attrs_end,
                descriptor_count, attrs_count,
                cc_descriptors_count, cc_descr_attrs_count);
-       plugin_set = bt_plugin_set_create();
-       if (!plugin_set) {
+       *plugin_set_out = bt_plugin_set_create();
+       if (!*plugin_set_out) {
                BT_LOGE_STR("Cannot create empty plugin set.");
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto error;
        }
 
@@ -1251,73 +1274,91 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_sections(
                        descriptor->name, descriptor->major, descriptor->minor);
 
                if (descriptor->major > __BT_PLUGIN_VERSION_MAJOR) {
-                       /*
-                        * INFO-level logging because we're only
-                        * _trying_ to open this file as a compatible
-                        * Babeltrace plugin: if it's not, it's not an
-                        * error. And because this can be tried during
-                        * bt_plugin_find_all_from_dir(), it's not even
-                        * a warning.
-                        */
-                       BT_LOGI("Unknown ABI major version: abi-major=%d",
-                               descriptor->major);
-                       goto error;
+                       BT_LOG_WRITE(fail_on_load_error ? BT_LOG_WARN :
+                               BT_LOG_INFO, BT_LOG_TAG,
+                               "Unknown ABI major version: abi-major=%d",
+                                       descriptor->major);
+
+                       if (fail_on_load_error) {
+                               status = BT_PLUGIN_STATUS_LOADING_ERROR;
+                               goto error;
+                       } else {
+                               continue;
+                       }
                }
 
                plugin = bt_plugin_so_create_empty(shared_lib_handle);
                if (!plugin) {
                        BT_LOGE_STR("Cannot create empty shared library handle.");
+                       status = BT_PLUGIN_STATUS_NOMEM;
                        goto error;
                }
 
                if (shared_lib_handle && shared_lib_handle->path) {
-                       bt_plugin_set_path(plugin, shared_lib_handle->path->str);
+                       bt_plugin_set_path(plugin,
+                               shared_lib_handle->path->str);
                }
 
-               status = bt_plugin_so_init(plugin, descriptor, attrs_begin,
-                       attrs_end, cc_descriptors_begin, cc_descriptors_end,
+               status = bt_plugin_so_init(plugin, fail_on_load_error,
+                       descriptor, attrs_begin, attrs_end,
+                       cc_descriptors_begin, cc_descriptors_end,
                        cc_descr_attrs_begin, cc_descr_attrs_end);
-               if (status < 0) {
+               if (status == BT_PLUGIN_STATUS_OK) {
+                       /* Add to plugin set */
+                       bt_plugin_set_add_plugin(*plugin_set_out, plugin);
+                       BT_OBJECT_PUT_REF_AND_RESET(plugin);
+               } else if (status < 0) {
                        /*
-                        * DEBUG-level logging because we're only
-                        * _trying_ to open this file as a compatible
-                        * Babeltrace plugin: if it's not, it's not an
-                        * error. And because this can be tried during
-                        * bt_plugin_find_all_from_dir(), it's not
-                        * even a warning.
+                        * bt_plugin_so_init() handles
+                        * `fail_on_load_error`, so this is a "real"
+                        * error.
                         */
-                       BT_LOGD_STR("Cannot initialize SO plugin object from sections.");
+                       BT_LOGW_STR("Cannot initialize SO plugin object from sections.");
                        BT_OBJECT_PUT_REF_AND_RESET(plugin);
                        goto error;
                }
 
-               /* Add to plugin set */
-               bt_plugin_set_add_plugin(plugin_set, plugin);
-               bt_object_put_ref(plugin);
+               BT_ASSERT(!plugin);
+       }
+
+       BT_ASSERT(*plugin_set_out);
+
+       if ((*plugin_set_out)->plugins->len == 0) {
+               BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
        }
 
        goto end;
 
 error:
-       BT_OBJECT_PUT_REF_AND_RESET(plugin_set);
+       BT_ASSERT(status < 0);
+       BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
 
 end:
-       return plugin_set;
+       return status;
 }
 
 BT_HIDDEN
-struct bt_plugin_set *bt_plugin_so_create_all_from_static(void)
+enum bt_plugin_status bt_plugin_so_create_all_from_static(
+               bool fail_on_load_error,
+               struct bt_plugin_set **plugin_set_out)
 {
-       struct bt_plugin_set *plugin_set = NULL;
-       struct bt_plugin_so_shared_lib_handle *shared_lib_handle =
-               bt_plugin_so_shared_lib_handle_create(NULL);
+       enum bt_plugin_status status;
+       struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL;
 
-       if (!shared_lib_handle) {
+       BT_ASSERT(plugin_set_out);
+       *plugin_set_out = NULL;
+       status = bt_plugin_so_shared_lib_handle_create(NULL,
+               &shared_lib_handle);
+       if (status != BT_PLUGIN_STATUS_OK) {
+               BT_ASSERT(!shared_lib_handle);
                goto end;
        }
 
+       BT_ASSERT(shared_lib_handle);
        BT_LOGD_STR("Creating all SO plugins from built-in plugins.");
-       plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle,
+       status = bt_plugin_so_create_all_from_sections(shared_lib_handle,
+               fail_on_load_error,
                __bt_get_begin_section_plugin_descriptors(),
                __bt_get_end_section_plugin_descriptors(),
                __bt_get_begin_section_plugin_descriptor_attributes(),
@@ -1325,19 +1366,22 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_static(void)
                __bt_get_begin_section_component_class_descriptors(),
                __bt_get_end_section_component_class_descriptors(),
                __bt_get_begin_section_component_class_descriptor_attributes(),
-               __bt_get_end_section_component_class_descriptor_attributes());
+               __bt_get_end_section_component_class_descriptor_attributes(),
+               plugin_set_out);
+       BT_ASSERT((status == BT_PLUGIN_STATUS_OK && *plugin_set_out &&
+               (*plugin_set_out)->plugins->len > 0) || !*plugin_set_out);
 
 end:
        BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle);
-
-       return plugin_set;
+       return status;
 }
 
 BT_HIDDEN
-struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
+enum bt_plugin_status bt_plugin_so_create_all_from_file(const char *path,
+               bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
 {
        size_t path_len;
-       struct bt_plugin_set *plugin_set = NULL;
+       enum bt_plugin_status status;
        struct __bt_plugin_descriptor const * const *descriptors_begin = NULL;
        struct __bt_plugin_descriptor const * const *descriptors_end = NULL;
        struct __bt_plugin_descriptor_attribute const * const *attrs_begin = NULL;
@@ -1358,6 +1402,8 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
        struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL;
 
        BT_ASSERT(path);
+       BT_ASSERT(plugin_set_out);
+       *plugin_set_out = NULL;
        path_len = strlen(path);
        BT_ASSERT_PRE(path_len > PLUGIN_SUFFIX_LEN,
                "Path length is too short: path-length=%zu, min-length=%zu",
@@ -1377,14 +1423,17 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
                NATIVE_PLUGIN_SUFFIX_LEN);
        if (!is_shared_object && !is_libtool_wrapper) {
                /* Name indicates this is not a plugin file; not an error */
-               BT_LOGI("File is not a SO plugin file: path=\"%s\"", path);
+               BT_LOGI("File is not an SO plugin file: path=\"%s\"", path);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
-       shared_lib_handle = bt_plugin_so_shared_lib_handle_create(path);
-       if (!shared_lib_handle) {
+       status = bt_plugin_so_shared_lib_handle_create(path,
+               &shared_lib_handle);
+       if (status != BT_PLUGIN_STATUS_OK) {
                /* bt_plugin_so_shared_lib_handle_create() logs more details */
-               BT_LOGD_STR("Cannot create shared library handle.");
+               BT_ASSERT(!shared_lib_handle);
+               BT_LOGE_STR("Cannot create shared library handle.");
                goto end;
        }
 
@@ -1392,19 +1441,34 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
                        (gpointer *) &get_begin_section_plugin_descriptors)) {
                descriptors_begin = get_begin_section_plugin_descriptors();
        } else {
+               /*
+                * Use this first symbol to know whether or not this
+                * shared object _looks like_ a Babeltrace plugin. Since
+                * g_module_symbol() failed, assume that this is not a
+                * Babeltrace plugin, so it's not an error.
+                */
                BT_LOGI("Cannot resolve plugin symbol: path=\"%s\", "
                        "symbol=\"%s\"", path,
                        "__bt_get_begin_section_plugin_descriptors");
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
+       /*
+        * If g_module_symbol() fails for any of the other symbols, fail
+        * if `fail_on_load_error` is true.
+        */
        if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_plugin_descriptors",
                        (gpointer *) &get_end_section_plugin_descriptors)) {
                descriptors_end = get_end_section_plugin_descriptors();
        } else {
-               BT_LOGI("Cannot resolve plugin symbol: path=\"%s\", "
+               BT_LOG_WRITE(fail_on_load_error ? BT_LOG_WARN : BT_LOG_INFO,
+                       BT_LOG_TAG,
+                       "Cannot resolve plugin symbol: path=\"%s\", "
                        "symbol=\"%s\"", path,
                        "__bt_get_end_section_plugin_descriptors");
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
@@ -1427,13 +1491,17 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
        }
 
        if ((!!attrs_begin - !!attrs_end) != 0) {
-               BT_LOGI("Found section start or end symbol, but not both: "
+               BT_LOG_WRITE(fail_on_load_error ? BT_LOG_WARN : BT_LOG_INFO,
+                       BT_LOG_TAG,
+                       "Found section start or end symbol, but not both: "
                        "path=\"%s\", symbol-start=\"%s\", "
                        "symbol-end=\"%s\", symbol-start-addr=%p, "
                        "symbol-end-addr=%p",
                        path, "__bt_get_begin_section_plugin_descriptor_attributes",
                        "__bt_get_end_section_plugin_descriptor_attributes",
                        attrs_begin, attrs_end);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
@@ -1456,13 +1524,17 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
        }
 
        if ((!!cc_descriptors_begin - !!cc_descriptors_end) != 0) {
-               BT_LOGI("Found section start or end symbol, but not both: "
+               BT_LOG_WRITE(fail_on_load_error ? BT_LOG_WARN : BT_LOG_INFO,
+                       BT_LOG_TAG,
+                       "Found section start or end symbol, but not both: "
                        "path=\"%s\", symbol-start=\"%s\", "
                        "symbol-end=\"%s\", symbol-start-addr=%p, "
                        "symbol-end-addr=%p",
                        path, "__bt_get_begin_section_component_class_descriptors",
                        "__bt_get_end_section_component_class_descriptors",
                        cc_descriptors_begin, cc_descriptors_end);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
@@ -1485,26 +1557,31 @@ struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path)
        }
 
        if ((!!cc_descr_attrs_begin - !!cc_descr_attrs_end) != 0) {
-               BT_LOGI("Found section start or end symbol, but not both: "
+               BT_LOG_WRITE(fail_on_load_error ? BT_LOG_WARN : BT_LOG_INFO,
+                       BT_LOG_TAG,
+                       "Found section start or end symbol, but not both: "
                        "path=\"%s\", symbol-start=\"%s\", "
                        "symbol-end=\"%s\", symbol-start-addr=%p, "
                        "symbol-end-addr=%p",
                        path, "__bt_get_begin_section_component_class_descriptor_attributes",
                        "__bt_get_end_section_component_class_descriptor_attributes",
                        cc_descr_attrs_begin, cc_descr_attrs_end);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto end;
        }
 
        /* Initialize plugin */
        BT_LOGD_STR("Initializing plugin object.");
-       plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle,
+       status = bt_plugin_so_create_all_from_sections(shared_lib_handle,
+               fail_on_load_error,
                descriptors_begin, descriptors_end, attrs_begin, attrs_end,
                cc_descriptors_begin, cc_descriptors_end,
-               cc_descr_attrs_begin, cc_descr_attrs_end);
+               cc_descr_attrs_begin, cc_descr_attrs_end, plugin_set_out);
 
 end:
        BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle);
-       return plugin_set;
+       return status;
 }
 
 static
index 328cc1480e5cae988c6393c23ebf85d0811070f8..a2259c8b017b57842f49ea31dad620c861653b4f 100644 (file)
@@ -53,10 +53,13 @@ struct bt_plugin_so_spec_data {
 };
 
 BT_HIDDEN
-struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path);
+enum bt_plugin_status bt_plugin_so_create_all_from_file(const char *path,
+               bool fail_on_load_error, struct bt_plugin_set **plugin_set_out);
 
 BT_HIDDEN
-struct bt_plugin_set *bt_plugin_so_create_all_from_static(void);
+enum bt_plugin_status bt_plugin_so_create_all_from_static(
+               bool fail_on_load_error,
+               struct bt_plugin_set **plugin_set_out);
 
 void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin,
                struct bt_component_class *comp_class);
index c0cd158abaecd5010cb7741fe24f369ab326b672..47b49cf993caf2856a19531a6189f37a0cd94b72 100644 (file)
 #include <plugin/python-plugin-provider.h>
 
 static
-struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path) =
+enum bt_plugin_status (*bt_plugin_python_create_all_from_file_sym)(
+               const char *path, bool fail_on_load_error,
+               struct bt_plugin_set **plugin_set_out) =
        bt_plugin_python_create_all_from_file;
 
 static
-void init_python_plugin_provider(void) {}
+void init_python_plugin_provider(void)
+{
+}
 #else /* BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT */
 static GModule *python_plugin_provider_module;
+
 static
-struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path);
+enum bt_plugin_status (*bt_plugin_python_create_all_from_file_sym)(
+                const char *path, bool fail_on_load_error,
+                struct bt_plugin_set **plugin_set_out);
 
 static
 void init_python_plugin_provider(void) {
@@ -77,6 +84,11 @@ void init_python_plugin_provider(void) {
        python_plugin_provider_module =
                g_module_open(PYTHON_PLUGIN_PROVIDER_FILENAME, 0);
        if (!python_plugin_provider_module) {
+               /*
+                * This is not an error. The whole point of having an
+                * external Python plugin provider is that it can be
+                * missing and the Babeltrace library still works.
+                */
                BT_LOGI("Cannot open `%s`: %s: continuing without Python plugin support.",
                        PYTHON_PLUGIN_PROVIDER_FILENAME, g_module_error());
                return;
@@ -85,8 +97,15 @@ void init_python_plugin_provider(void) {
        if (!g_module_symbol(python_plugin_provider_module,
                        PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR,
                        (gpointer) &bt_plugin_python_create_all_from_file_sym)) {
-               BT_LOGI("Cannot find the Python plugin provider loading symbol: continuing without Python plugin support: "
+               /*
+                * This is an error because, since we found the Python
+                * plugin provider shared object, we expect this symbol
+                * to exist.
+                */
+               BT_LOGE("Cannot find the Python plugin provider loading symbol: "
+                       "%s: continuing without Python plugin support: "
                        "file=\"%s\", symbol=\"%s\"",
+                       g_module_error(),
                        PYTHON_PLUGIN_PROVIDER_FILENAME,
                        PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR);
                return;
@@ -125,45 +144,70 @@ const struct bt_plugin *bt_plugin_set_borrow_plugin_by_index_const(
        return g_ptr_array_index(plugin_set->plugins, index);
 }
 
-const struct bt_plugin_set *bt_plugin_find_all_from_static(void)
+enum bt_plugin_status bt_plugin_find_all_from_static(
+               bt_bool fail_on_load_error,
+               const struct bt_plugin_set **plugin_set_out)
 {
        /* bt_plugin_so_create_all_from_static() logs errors */
-       return bt_plugin_so_create_all_from_static();
+       return bt_plugin_so_create_all_from_static(fail_on_load_error,
+               (void *) plugin_set_out);
 }
 
-const struct bt_plugin_set *bt_plugin_find_all_from_file(const char *path)
+enum bt_plugin_status bt_plugin_find_all_from_file(const char *path,
+               bt_bool fail_on_load_error,
+               const struct bt_plugin_set **plugin_set_out)
 {
-       struct bt_plugin_set *plugin_set = NULL;
+       enum bt_plugin_status status;
 
        BT_ASSERT_PRE_NON_NULL(path, "Path");
+       BT_ASSERT_PRE_NON_NULL(path, "Plugin set (output)");
        BT_LOGI("Creating plugins from file: path=\"%s\"", path);
 
        /* Try shared object plugins */
-       plugin_set = bt_plugin_so_create_all_from_file(path);
-       if (plugin_set) {
+       status = bt_plugin_so_create_all_from_file(path, fail_on_load_error,
+               (void *) plugin_set_out);
+       if (status == BT_PLUGIN_STATUS_OK) {
+               BT_ASSERT(*plugin_set_out);
+               BT_ASSERT((*plugin_set_out)->plugins->len > 0);
+               goto end;
+       } else if (status < 0) {
+               BT_ASSERT(!*plugin_set_out);
                goto end;
        }
 
+       BT_ASSERT(status == BT_PLUGIN_STATUS_NOT_FOUND);
+       BT_ASSERT(!*plugin_set_out);
+
        /* Try Python plugins if support is available */
        init_python_plugin_provider();
        if (bt_plugin_python_create_all_from_file_sym) {
-               plugin_set = bt_plugin_python_create_all_from_file_sym(path);
-               if (plugin_set) {
+               status = bt_plugin_python_create_all_from_file_sym(path,
+                       fail_on_load_error, (void *) plugin_set_out);
+               if (status == BT_PLUGIN_STATUS_OK) {
+                       BT_ASSERT(*plugin_set_out);
+                       BT_ASSERT((*plugin_set_out)->plugins->len > 0);
+                       goto end;
+               } else if (status < 0) {
+                       BT_ASSERT(!*plugin_set_out);
                        goto end;
                }
+
+               BT_ASSERT(status == BT_PLUGIN_STATUS_NOT_FOUND);
+               BT_ASSERT(!*plugin_set_out);
        }
 
 end:
-       if (plugin_set) {
+       if (status == BT_PLUGIN_STATUS_OK) {
                BT_LOGI("Created %u plugins from file: "
                        "path=\"%s\", count=%u, plugin-set-addr=%p",
-                       plugin_set->plugins->len, path,
-                       plugin_set->plugins->len, plugin_set);
-       } else {
+                       (*plugin_set_out)->plugins->len, path,
+                       (*plugin_set_out)->plugins->len,
+                       *plugin_set_out);
+       } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
                BT_LOGI("Found no plugins in file: path=\"%s\"", path);
        }
 
-       return plugin_set;
+       return status;
 }
 
 static void destroy_gstring(void *data)
@@ -171,7 +215,8 @@ static void destroy_gstring(void *data)
        g_string_free(data, TRUE);
 }
 
-const struct bt_plugin *bt_plugin_find(const char *plugin_name)
+enum bt_plugin_status bt_plugin_find(const char *plugin_name,
+               bt_bool fail_on_load_error, const struct bt_plugin **plugin_out)
 {
        const char *system_plugin_dir;
        char *home_plugin_dir = NULL;
@@ -180,14 +225,17 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
        const struct bt_plugin_set *plugin_set = NULL;
        GPtrArray *dirs = NULL;
        int ret;
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
        size_t i, j;
 
        BT_ASSERT_PRE_NON_NULL(plugin_name, "Name");
+       BT_ASSERT_PRE_NON_NULL(plugin_out, "Plugin (output)");
        BT_LOGI("Finding named plugin in standard directories and built-in plugins: "
                "name=\"%s\"", plugin_name);
        dirs = g_ptr_array_new_with_free_func((GDestroyNotify) destroy_gstring);
        if (!dirs) {
                BT_LOGE_STR("Failed to allocate a GPtrArray.");
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto end;
        }
 
@@ -210,17 +258,18 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
                ret = bt_common_append_plugin_path_dirs(envvar, dirs);
                if (ret) {
                        BT_LOGE_STR("Failed to append plugin path to array of directories.");
+                       status = BT_PLUGIN_STATUS_NOMEM;
                        goto end;
                }
        }
 
        home_plugin_dir = bt_common_get_home_plugin_path(BT_LOG_OUTPUT_LEVEL);
        if (home_plugin_dir) {
-               GString *home_plugin_dir_str =
-                       g_string_new(home_plugin_dir);
+               GString *home_plugin_dir_str = g_string_new(home_plugin_dir);
 
                if (!home_plugin_dir_str) {
                        BT_LOGE_STR("Failed to allocate a GString.");
+                       status = BT_PLUGIN_STATUS_NOMEM;
                        goto end;
                }
 
@@ -234,6 +283,7 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
 
                if (!system_plugin_dir_str) {
                        BT_LOGE_STR("Failed to allocate a GString.");
+                       status = BT_PLUGIN_STATUS_NOMEM;
                        goto end;
                }
 
@@ -256,13 +306,21 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
                }
 
                /* bt_plugin_find_all_from_dir() logs details/errors */
-               plugin_set = bt_plugin_find_all_from_dir(dir->str, BT_FALSE);
-               if (!plugin_set) {
+               status = bt_plugin_find_all_from_dir(dir->str, BT_FALSE,
+                       fail_on_load_error, &plugin_set);
+               if (status < 0) {
+                       BT_ASSERT(!plugin_set);
+                       goto end;
+               } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+                       BT_ASSERT(!plugin_set);
                        BT_LOGI("No plugins found in directory: path=\"%s\"",
                                dir->str);
                        continue;
                }
 
+               BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+               BT_ASSERT(plugin_set);
+
                for (j = 0; j < plugin_set->plugins->len; j++) {
                        const struct bt_plugin *candidate_plugin =
                                g_ptr_array_index(plugin_set->plugins, j);
@@ -271,8 +329,8 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
                                        plugin_name) == 0) {
                                BT_LOGI("Plugin found in directory: name=\"%s\", path=\"%s\"",
                                        plugin_name, dir->str);
-                               plugin = candidate_plugin;
-                               bt_object_get_no_null_check(plugin);
+                               *plugin_out = candidate_plugin;
+                               bt_object_get_no_null_check(*plugin_out);
                                goto end;
                        }
                }
@@ -281,24 +339,39 @@ const struct bt_plugin *bt_plugin_find(const char *plugin_name)
                        plugin_name, dir->str);
        }
 
-       bt_object_put_ref(plugin_set);
-       plugin_set = bt_plugin_find_all_from_static();
-       if (plugin_set) {
-               for (j = 0; j < plugin_set->plugins->len; j++) {
-                       const struct bt_plugin *candidate_plugin =
-                               g_ptr_array_index(plugin_set->plugins, j);
+       BT_OBJECT_PUT_REF_AND_RESET(plugin_set);
+       status = bt_plugin_find_all_from_static(fail_on_load_error,
+               &plugin_set);
+       if (status < 0) {
+               BT_ASSERT(!plugin_set);
+               goto end;
+       }
 
-                       if (strcmp(bt_plugin_get_name(candidate_plugin),
-                                       plugin_name) == 0) {
-                               BT_LOGI("Plugin found in built-in plugins: "
-                                       "name=\"%s\"", plugin_name);
-                               plugin = candidate_plugin;
-                               bt_object_get_no_null_check(plugin);
-                               goto end;
-                       }
+       if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+               BT_ASSERT(!plugin_set);
+               BT_LOGI_STR("No plugins found in built-in plugins.");
+               goto end;
+       }
+
+       BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+       BT_ASSERT(plugin_set);
+
+       for (j = 0; j < plugin_set->plugins->len; j++) {
+               const struct bt_plugin *candidate_plugin =
+                       g_ptr_array_index(plugin_set->plugins, j);
+
+               if (strcmp(bt_plugin_get_name(candidate_plugin),
+                               plugin_name) == 0) {
+                       BT_LOGI("Plugin found in built-in plugins: "
+                               "name=\"%s\"", plugin_name);
+                       *plugin_out = candidate_plugin;
+                       bt_object_get_no_null_check(*plugin_out);
+                       goto end;
                }
        }
 
+       status = BT_PLUGIN_STATUS_NOT_FOUND;
+
 end:
        free(home_plugin_dir);
        bt_object_put_ref(plugin_set);
@@ -307,28 +380,30 @@ end:
                g_ptr_array_free(dirs, TRUE);
        }
 
-       if (plugin) {
+       if (status == BT_PLUGIN_STATUS_OK) {
                BT_LIB_LOGI("Found plugin in standard directories and built-in plugins: "
                        "%!+l", plugin);
-       } else {
+       } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
                BT_LOGI("No plugin found in standard directories and built-in plugins: "
                        "name=\"%s\"", plugin_name);
        }
 
-       return plugin;
+       return status;
 }
 
 static struct {
        pthread_mutex_t lock;
        struct bt_plugin_set *plugin_set;
        bool recurse;
+       bool fail_on_load_error;
+       enum bt_plugin_status status;
 } append_all_from_dir_info = {
        .lock = PTHREAD_MUTEX_INITIALIZER
 };
 
 static
-int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag,
-               struct FTW *s)
+int nftw_append_all_from_dir(const char *file,
+               const struct stat *sb, int flag, struct FTW *s)
 {
        int ret = 0;
        const char *name = file + s->base;
@@ -341,7 +416,7 @@ int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag,
        switch (flag) {
        case FTW_F:
        {
-               const struct bt_plugin_set *plugins_from_file;
+               const struct bt_plugin_set *plugins_from_file = NULL;
 
                if (name[0] == '.') {
                        /* Skip hidden files */
@@ -349,11 +424,15 @@ int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag,
                        goto end;
                }
 
-               plugins_from_file = bt_plugin_find_all_from_file(file);
-
-               if (plugins_from_file) {
+               append_all_from_dir_info.status =
+                       bt_plugin_find_all_from_file(file,
+                               append_all_from_dir_info.fail_on_load_error,
+                               &plugins_from_file);
+               if (append_all_from_dir_info.status == BT_PLUGIN_STATUS_OK) {
                        size_t j;
 
+                       BT_ASSERT(plugins_from_file);
+
                        for (j = 0; j < plugins_from_file->plugins->len; j++) {
                                struct bt_plugin *plugin =
                                        g_ptr_array_index(plugins_from_file->plugins, j);
@@ -367,12 +446,26 @@ int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag,
                        }
 
                        bt_object_put_ref(plugins_from_file);
+                       goto end;
+               } else if (append_all_from_dir_info.status < 0) {
+                       /* bt_plugin_find_all_from_file() logs errors */
+                       BT_ASSERT(!plugins_from_file);
+                       ret = -1;
+                       goto end;
                }
+
+               /*
+                * Not found in this file: this is no an error; continue
+                * walking the directories.
+                */
+               BT_ASSERT(!plugins_from_file);
+               BT_ASSERT(append_all_from_dir_info.status ==
+                       BT_PLUGIN_STATUS_NOT_FOUND);
                break;
        }
        case FTW_DNR:
                /* Continue to next file / directory. */
-               BT_LOGW("Cannot enter directory: continuing: path=\"%s\"", file);
+               BT_LOGI("Cannot enter directory: continuing: path=\"%s\"", file);
                break;
        case FTW_NS:
                /* Continue to next file / directory. */
@@ -387,10 +480,11 @@ end:
 static
 enum bt_plugin_status bt_plugin_create_append_all_from_dir(
                struct bt_plugin_set *plugin_set, const char *path,
-               bt_bool recurse)
+               bt_bool recurse, bt_bool fail_on_load_error)
 {
        int nftw_flags = FTW_PHYS;
-       enum bt_plugin_status ret = BT_PLUGIN_STATUS_OK;
+       int ret;
+       enum bt_plugin_status status;
 
        BT_ASSERT(plugin_set);
        BT_ASSERT(path);
@@ -398,50 +492,87 @@ enum bt_plugin_status bt_plugin_create_append_all_from_dir(
        pthread_mutex_lock(&append_all_from_dir_info.lock);
        append_all_from_dir_info.plugin_set = plugin_set;
        append_all_from_dir_info.recurse = recurse;
+       append_all_from_dir_info.status = BT_PLUGIN_STATUS_OK;
+       append_all_from_dir_info.fail_on_load_error = fail_on_load_error;
        ret = nftw(path, nftw_append_all_from_dir,
                APPEND_ALL_FROM_DIR_NFDOPEN_MAX, nftw_flags);
+       append_all_from_dir_info.plugin_set = NULL;
+       status = append_all_from_dir_info.status;
        pthread_mutex_unlock(&append_all_from_dir_info.lock);
-       if (ret != 0) {
-               BT_LOGW_ERRNO("Cannot open directory", ": path=\"%s\"", path);
-               ret = BT_PLUGIN_STATUS_ERROR;
+       if (ret) {
+               BT_LOGW_ERRNO("Failed to walk directory",
+                       ": path=\"%s\", recurse=%d",
+                       path, recurse);
+               status = BT_PLUGIN_STATUS_ERROR;
+               goto end;
+       }
+
+       if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+               /*
+                * We're just appending in this function; even if
+                * nothing was found, it's still okay from the caller's
+                * perspective.
+                */
+               status = BT_PLUGIN_STATUS_OK;
        }
 
+end:
        return ret;
 }
 
-const struct bt_plugin_set *bt_plugin_find_all_from_dir(const char *path,
-               bt_bool recurse)
+enum bt_plugin_status bt_plugin_find_all_from_dir(const char *path,
+               bt_bool recurse, bt_bool fail_on_load_error,
+               const struct bt_plugin_set **plugin_set_out)
 {
-       struct bt_plugin_set *plugin_set;
-       enum bt_plugin_status status;
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
 
+       BT_ASSERT_PRE_NON_NULL(plugin_set_out, "Plugin set (output)");
        BT_LOGI("Creating all plugins in directory: path=\"%s\", recurse=%d",
                path, recurse);
-       plugin_set = bt_plugin_set_create();
-       if (!plugin_set) {
+       *plugin_set_out = bt_plugin_set_create();
+       if (!*plugin_set_out) {
                BT_LOGE_STR("Cannot create empty plugin set.");
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto error;
        }
 
-       /* Append found plugins to array */
-       status = bt_plugin_create_append_all_from_dir(plugin_set, path,
-               recurse);
+       /*
+        * Append found plugins to array (never returns
+        * `BT_PLUGIN_STATUS_NOT_FOUND`)
+        */
+       status = bt_plugin_create_append_all_from_dir((void *) *plugin_set_out,
+               path, recurse, fail_on_load_error);
        if (status < 0) {
+               /*
+                * bt_plugin_create_append_all_from_dir() handles
+                * `fail_on_load_error`, so this is a "real" error.
+                */
                BT_LOGW("Cannot append plugins found in directory: "
                        "path=\"%s\", status=%s",
                        path, bt_plugin_status_string(status));
                goto error;
        }
 
+       BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+
+       if ((*plugin_set_out)->plugins->len == 0) {
+               /* Nothing was appended: not found */
+               BT_LOGI("No plugins found in directory: path=\"%s\"", path);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
+               goto error;
+       }
+
        BT_LOGI("Created %u plugins from directory: count=%u, path=\"%s\"",
-               plugin_set->plugins->len, plugin_set->plugins->len, path);
+               (*plugin_set_out)->plugins->len,
+               (*plugin_set_out)->plugins->len, path);
        goto end;
 
 error:
-       BT_OBJECT_PUT_REF_AND_RESET(plugin_set);
+       BT_ASSERT(status != BT_PLUGIN_STATUS_OK);
+       BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
 
 end:
-       return plugin_set;
+       return status;
 }
 
 const char *bt_plugin_get_name(const struct bt_plugin *plugin)
index ed2fc4a75cd4702fe2398249063f6a1b9c339c86..46b2b117be7b9b089db11687f2fad155c4c88b0f 100644 (file)
@@ -45,12 +45,6 @@ enum bt_plugin_type {
        BT_PLUGIN_TYPE_PYTHON = 1,
 };
 
-enum bt_plugin_status {
-       BT_PLUGIN_STATUS_OK = 0,
-       BT_PLUGIN_STATUS_ERROR = -1,
-       BT_PLUGIN_STATUS_NOMEM = -12,
-};
-
 struct bt_plugin {
        struct bt_object base;
        enum bt_plugin_type type;
@@ -99,8 +93,12 @@ const char *bt_plugin_status_string(enum bt_plugin_status status)
        switch (status) {
        case BT_PLUGIN_STATUS_OK:
                return "BT_PLUGIN_STATUS_OK";
+       case BT_PLUGIN_STATUS_NOT_FOUND:
+               return "BT_PLUGIN_STATUS_NOT_FOUND";
        case BT_PLUGIN_STATUS_ERROR:
                return "BT_PLUGIN_STATUS_ERROR";
+       case BT_PLUGIN_STATUS_LOADING_ERROR:
+               return "BT_PLUGIN_STATUS_LOADING_ERROR";
        case BT_PLUGIN_STATUS_NOMEM:
                return "BT_PLUGIN_STATUS_NOMEM";
        default:
index 6f775cb00ee3948b18f2e54e52a49ef4732ac020..42cc642f00a4eaf442fd8eaf04ab1f3c4bf889e6 100644 (file)
@@ -53,16 +53,23 @@ enum python_state {
 
        /* init_python() called once without success */
        PYTHON_STATE_CANNOT_INITIALIZE,
+
+       /*
+        * init_python() called, but environment variable asks the
+        * Python interpreter not to be loaded.
+        */
+       PYTHON_STATE_WONT_INITIALIZE,
 } python_state = PYTHON_STATE_NOT_INITED;
 
 static PyObject *py_try_load_plugin_module_func = NULL;
 static bool python_was_initialized_by_us;
 
 static
-void print_python_traceback_warn(void)
+void print_python_traceback(int log_level)
 {
-       if (BT_LOG_ON_WARN && Py_IsInitialized() && PyErr_Occurred()) {
-               BT_LOGW_STR("Exception occured: traceback: ");
+       if (BT_LOG_ON(log_level) && Py_IsInitialized() && PyErr_Occurred()) {
+               BT_LOG_WRITE(log_level, BT_LOG_TAG,
+                       "Exception occured: Python traceback: ");
                PyErr_Print();
        }
 }
@@ -96,7 +103,7 @@ void init_python(void)
        dis_python_env = getenv("BABELTRACE_DISABLE_PYTHON_PLUGINS");
        if (dis_python_env && strcmp(dis_python_env, "1") == 0) {
                BT_LOGI_STR("Python plugin support is disabled because `BABELTRACE_DISABLE_PYTHON_PLUGINS=1`.");
-               python_state = PYTHON_STATE_CANNOT_INITIALIZE;
+               python_state = PYTHON_STATE_WONT_INITIALIZE;
                goto end;
        }
 
@@ -113,7 +120,7 @@ void init_python(void)
 
        py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin");
        if (!py_bt2_py_plugin_mod) {
-               BT_LOGI_STR("Cannot import bt2.py_plugin Python module: Python plugin support is disabled.");
+               BT_LOGW_STR("Cannot import bt2.py_plugin Python module: Python plugin support is disabled.");
                python_state = PYTHON_STATE_CANNOT_INITIALIZE;
                goto end;
        }
@@ -121,7 +128,7 @@ void init_python(void)
        py_try_load_plugin_module_func =
                PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module");
        if (!py_try_load_plugin_module_func) {
-               BT_LOGI_STR("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module: Python plugin support is disabled.");
+               BT_LOGW_STR("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module: Python plugin support is disabled.");
                python_state = PYTHON_STATE_CANNOT_INITIALIZE;
                goto end;
        }
@@ -135,7 +142,7 @@ end:
        }
 #endif
 
-       print_python_traceback_warn();
+       print_python_traceback(BT_LOG_WARN);
        pyerr_clear();
        Py_XDECREF(py_bt2_py_plugin_mod);
        return;
@@ -157,9 +164,10 @@ void fini_python(void) {
 }
 
 static
-bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
+enum bt_plugin_status bt_plugin_from_python_plugin_info(PyObject *plugin_info,
+               bool fail_on_load_error, bt_plugin **plugin_out)
 {
-       bt_plugin *plugin = NULL;
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
        PyObject *py_name = NULL;
        PyObject *py_author = NULL;
        PyObject *py_description = NULL;
@@ -172,72 +180,111 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
        const char *license = NULL;
        unsigned int major = 0, minor = 0, patch = 0;
        const char *version_extra = NULL;
-       int ret;
 
+       BT_ASSERT(plugin_out);
+       *plugin_out = NULL;
        BT_ASSERT(plugin_info);
        BT_ASSERT(python_state == PYTHON_STATE_FULLY_INITIALIZED);
        py_name = PyObject_GetAttrString(plugin_info, "name");
        if (!py_name) {
-               BT_LOGW("Cannot find `name` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `name` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        py_author = PyObject_GetAttrString(plugin_info, "author");
        if (!py_author) {
-               BT_LOGW("Cannot find `author` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `author` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        py_description = PyObject_GetAttrString(plugin_info, "description");
        if (!py_description) {
-               BT_LOGW("Cannot find `desciption` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `desciption` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        py_license = PyObject_GetAttrString(plugin_info, "license");
        if (!py_license) {
-               BT_LOGW("Cannot find `license` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `license` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        py_version = PyObject_GetAttrString(plugin_info, "version");
        if (!py_version) {
-               BT_LOGW("Cannot find `version` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `version` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        py_comp_class_addrs = PyObject_GetAttrString(plugin_info,
                "comp_class_addrs");
        if (!py_comp_class_addrs) {
-               BT_LOGW("Cannot find `comp_class_addrs` attribute in Python plugin info object: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        if (PyUnicode_Check(py_name)) {
                name = PyUnicode_AsUTF8(py_name);
                if (!name) {
-                       BT_LOGW("Cannot decode Python plugin name string: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "Cannot decode Python plugin name string: "
                                "py-plugin-info-addr=%p", plugin_info);
+                       status = fail_on_load_error ?
+                               BT_PLUGIN_STATUS_LOADING_ERROR :
+                               BT_PLUGIN_STATUS_NOT_FOUND;
                        goto error;
                }
        } else {
                /* Plugin name is mandatory */
-               BT_LOGW("Plugin name is not a string: "
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Plugin name is not a string: "
                        "py-plugin-info-addr=%p", plugin_info);
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        if (PyUnicode_Check(py_author)) {
                author = PyUnicode_AsUTF8(py_author);
                if (!author) {
-                       BT_LOGW("Cannot decode Python plugin author string: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "Cannot decode Python plugin author string: "
                                "py-plugin-info-addr=%p", plugin_info);
+                       status = fail_on_load_error ?
+                               BT_PLUGIN_STATUS_LOADING_ERROR :
+                               BT_PLUGIN_STATUS_NOT_FOUND;
                        goto error;
                }
        }
@@ -245,8 +292,13 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
        if (PyUnicode_Check(py_description)) {
                description = PyUnicode_AsUTF8(py_description);
                if (!description) {
-                       BT_LOGW("Cannot decode Python plugin description string: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "Cannot decode Python plugin description string: "
                                "py-plugin-info-addr=%p", plugin_info);
+                       status = fail_on_load_error ?
+                               BT_PLUGIN_STATUS_LOADING_ERROR :
+                               BT_PLUGIN_STATUS_NOT_FOUND;
                        goto error;
                }
        }
@@ -254,8 +306,13 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
        if (PyUnicode_Check(py_license)) {
                license = PyUnicode_AsUTF8(py_license);
                if (!license) {
-                       BT_LOGW("Cannot decode Python plugin license string: "
+                       BT_LOG_WRITE(fail_on_load_error ?
+                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                               "Cannot decode Python plugin license string: "
                                "py-plugin-info-addr=%p", plugin_info);
+                       status = fail_on_load_error ?
+                               BT_PLUGIN_STATUS_LOADING_ERROR :
+                               BT_PLUGIN_STATUS_NOT_FOUND;
                        goto error;
                }
        }
@@ -284,8 +341,13 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
 
                        if (PyErr_Occurred()) {
                                /* Overflow error, most probably */
-                               BT_LOGW("Invalid Python plugin version format: "
+                               BT_LOG_WRITE(fail_on_load_error ?
+                                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                                       "Invalid Python plugin version format: "
                                        "py-plugin-info-addr=%p", plugin_info);
+                               status = fail_on_load_error ?
+                                       BT_PLUGIN_STATUS_LOADING_ERROR :
+                                       BT_PLUGIN_STATUS_NOT_FOUND;
                                goto error;
                        }
                }
@@ -298,35 +360,41 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
                        if (PyUnicode_Check(py_extra)) {
                                version_extra = PyUnicode_AsUTF8(py_extra);
                                if (!version_extra) {
-                               BT_LOGW("Cannot decode Python plugin version's extra string: "
-                                       "py-plugin-info-addr=%p", plugin_info);
+                                       BT_LOG_WRITE(fail_on_load_error ?
+                                               BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                                               "Cannot decode Python plugin version's extra string: "
+                                               "py-plugin-info-addr=%p", plugin_info);
+                                       status = fail_on_load_error ?
+                                               BT_PLUGIN_STATUS_LOADING_ERROR :
+                                               BT_PLUGIN_STATUS_NOT_FOUND;
                                        goto error;
                                }
                        }
                }
        }
 
-       plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
-       if (!plugin) {
+       *plugin_out = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
+       if (!*plugin_out) {
                BT_LOGE_STR("Cannot create empty plugin object.");
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto error;
        }
 
-       bt_plugin_set_name(plugin, name);
+       bt_plugin_set_name(*plugin_out, name);
 
        if (description) {
-               bt_plugin_set_description(plugin, description);
+               bt_plugin_set_description(*plugin_out, description);
        }
 
        if (author) {
-               bt_plugin_set_author(plugin, author);
+               bt_plugin_set_author(*plugin_out, author);
        }
 
        if (license) {
-               bt_plugin_set_license(plugin, license);
+               bt_plugin_set_license(*plugin_out, license);
        }
 
-       bt_plugin_set_version(plugin, major, minor, patch, version_extra);
+       bt_plugin_set_version(*plugin_out, major, minor, patch, version_extra);
 
        if (PyList_Check(py_comp_class_addrs)) {
                size_t i;
@@ -341,27 +409,36 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
                        if (PyLong_Check(py_comp_class_addr)) {
                                comp_class = PyLong_AsVoidPtr(py_comp_class_addr);
                        } else {
-                               BT_LOGW("Component class address is not an integer in Python plugin info object: "
+                               BT_LOG_WRITE(fail_on_load_error ?
+                                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                                       "Component class address is not an integer in Python plugin info object: "
                                        "py-plugin-info-addr=%p, index=%zu",
                                        plugin_info, i);
+
+                               if (fail_on_load_error) {
+                                       status = BT_PLUGIN_STATUS_LOADING_ERROR;
+                                       goto error;
+                               }
+
                                continue;
                        }
 
-                       ret = bt_plugin_add_component_class(plugin, comp_class);
-                       if (ret < 0) {
+                       status = bt_plugin_add_component_class(*plugin_out,
+                               comp_class);
+                       if (status < 0) {
                                BT_LOGE("Cannot add component class to plugin: "
                                        "py-plugin-info-addr=%p, "
                                        "plugin-addr=%p, plugin-name=\"%s\", "
                                        "comp-class-addr=%p, "
                                        "comp-class-name=\"%s\", "
                                        "comp-class-type=%s",
-                                       plugin_info,
-                                       plugin, bt_plugin_get_name(plugin),
+                                       plugin_info, *plugin_out,
+                                       bt_plugin_get_name(*plugin_out),
                                        comp_class,
                                        bt_component_class_get_name(comp_class),
                                        bt_component_class_type_string(
                                                bt_component_class_get_type(comp_class)));
-                               continue;
+                               goto error;
                        }
                }
        }
@@ -369,9 +446,10 @@ bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info)
        goto end;
 
 error:
-       print_python_traceback_warn();
+       BT_ASSERT(status != BT_PLUGIN_STATUS_OK);
+       print_python_traceback(fail_on_load_error ? BT_LOG_WARN : BT_LOG_INFO);
        pyerr_clear();
-       BT_OBJECT_PUT_REF_AND_RESET(plugin);
+       BT_OBJECT_PUT_REF_AND_RESET(*plugin_out);
 
 end:
        Py_XDECREF(py_name);
@@ -380,17 +458,18 @@ end:
        Py_XDECREF(py_license);
        Py_XDECREF(py_version);
        Py_XDECREF(py_comp_class_addrs);
-       return plugin;
+       return status;
 }
 
 G_MODULE_EXPORT
-bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
+enum bt_plugin_status bt_plugin_python_create_all_from_file(const char *path,
+               bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
 {
-       bt_plugin_set *plugin_set = NULL;
        bt_plugin *plugin = NULL;
        PyObject *py_plugin_info = NULL;
        gchar *basename = NULL;
        size_t path_len;
+       enum bt_plugin_status status = BT_PLUGIN_STATUS_OK;
 
        BT_ASSERT(path);
 
@@ -400,6 +479,15 @@ bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
                 * here because we already know Python cannot be fully
                 * initialized.
                 */
+               status = BT_PLUGIN_STATUS_ERROR;
+               goto error;
+       } else if (python_state == PYTHON_STATE_WONT_INITIALIZE) {
+               /*
+                * This is not an error: the environment requires that
+                * Python plugins are disabled, so it's simply not
+                * found.
+                */
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
@@ -412,13 +500,15 @@ bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
                        PYTHON_PLUGIN_FILE_EXT,
                        PYTHON_PLUGIN_FILE_EXT_LEN) != 0) {
                BT_LOGI("Skipping non-Python file: path=\"%s\"", path);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        /* File name starts with `bt_plugin_` */
        basename = g_path_get_basename(path);
        if (!basename) {
-               BT_LOGW("Cannot get path's basename: path=\"%s\"", path);
+               BT_LOGE("Cannot get path's basename: path=\"%s\"", path);
+               status = BT_PLUGIN_STATUS_ERROR;
                goto error;
        }
 
@@ -426,6 +516,7 @@ bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
                        PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) {
                BT_LOGI("Skipping Python file not starting with `%s`: "
                        "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX, path);
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
@@ -438,13 +529,22 @@ bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
         * this waiting time everytime you load the library.
         */
        init_python();
-       if (python_state != PYTHON_STATE_FULLY_INITIALIZED) {
+       if (python_state == PYTHON_STATE_WONT_INITIALIZE) {
+               /*
+                * This is not an error: the environment requires that
+                * Python plugins are disabled, so it's simply not
+                * found.
+                */
+               status = BT_PLUGIN_STATUS_NOT_FOUND;
+               goto error;
+       } else if (python_state != PYTHON_STATE_FULLY_INITIALIZED) {
                /*
                 * For some reason we cannot initialize Python,
                 * import the required modules, and get the required
                 * attributes from them.
                 */
-               BT_LOGI("Failed to initialize Python interpreter.");
+               BT_LOGE("Failed to initialize Python interpreter.");
+               status = BT_PLUGIN_STATUS_ERROR;
                goto error;
        }
 
@@ -458,42 +558,61 @@ bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path)
        py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func,
                "(s)", path);
        if (!py_plugin_info || py_plugin_info == Py_None) {
-               BT_LOGW("Cannot load Python plugin: path=\"%s\"", path);
-               print_python_traceback_warn();
+               BT_LOG_WRITE(fail_on_load_error ?
+                       BT_LOG_WARN : BT_LOG_INFO, BT_LOG_TAG,
+                       "Cannot load Python plugin: path=\"%s\"", path);
+               print_python_traceback(fail_on_load_error ? BT_LOG_WARN :
+                       BT_LOG_INFO);
                PyErr_Clear();
+               status = fail_on_load_error ? BT_PLUGIN_STATUS_LOADING_ERROR :
+                       BT_PLUGIN_STATUS_NOT_FOUND;
                goto error;
        }
 
        /*
         * Get bt_plugin from plugin info object.
         */
-       plugin = bt_plugin_from_python_plugin_info(py_plugin_info);
-       if (!plugin) {
+       plugin = NULL;
+       status = bt_plugin_from_python_plugin_info(py_plugin_info,
+               fail_on_load_error, &plugin);
+       if (status < 0) {
+               /*
+                * bt_plugin_from_python_plugin_info() handles
+                * `fail_on_load_error`, so this is a "real" error.
+                */
                BT_LOGW("Cannot create plugin object from Python plugin info object: "
                        "path=\"%s\", py-plugin-info-addr=%p",
                        path, py_plugin_info);
+               BT_ASSERT(!plugin);
+               goto error;
+       } else if (status == BT_PLUGIN_STATUS_NOT_FOUND) {
+               BT_ASSERT(!plugin);
                goto error;
        }
 
+       BT_ASSERT(status == BT_PLUGIN_STATUS_OK);
+       BT_ASSERT(plugin);
        bt_plugin_set_path(plugin, path);
-       plugin_set = bt_plugin_set_create();
-       if (!plugin_set) {
+       *plugin_set_out = bt_plugin_set_create();
+       if (!*plugin_set_out) {
                BT_LOGE_STR("Cannot create empty plugin set.");
+               status = BT_PLUGIN_STATUS_NOMEM;
                goto error;
        }
 
-       bt_plugin_set_add_plugin(plugin_set, plugin);
+       bt_plugin_set_add_plugin(*plugin_set_out, plugin);
        BT_LOGD("Created all Python plugins from file: path=\"%s\", "
                "plugin-addr=%p, plugin-name=\"%s\"",
                path, plugin, bt_plugin_get_name(plugin));
        goto end;
 
 error:
-       BT_OBJECT_PUT_REF_AND_RESET(plugin_set);
+       BT_ASSERT(status != BT_PLUGIN_STATUS_OK);
+       BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
 
 end:
        bt_plugin_put_ref(plugin);
        Py_XDECREF(py_plugin_info);
        g_free(basename);
-       return plugin_set;
+       return status;
 }
index 1d7aa2ce9c9aa05c09625adcc4d5ded4e1ee4025..a8848c743c7ae5db0279e24d2bb3f159345bbc12 100644 (file)
@@ -26,6 +26,7 @@
 #include <babeltrace2/plugin/plugin-const.h>
 
 extern
-struct bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path);
+enum bt_plugin_status bt_plugin_python_create_all_from_file(const char *path,
+               struct bt_plugin_set **plugin_set_out);
 
 #endif /* BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H */
index 4df92d0799dbe44a7b282e9127fb6a7ab57c5055..8384cf310baf4819d3b1cae73f18f340fc47d0d6 100644 (file)
@@ -47,9 +47,13 @@ class PluginSetTestCase(unittest.TestCase):
 
 
 class FindPluginsTestCase(unittest.TestCase):
-    def test_find_none(self):
-        pset = bt2.find_plugins('/this/does/not/exist/246703df-cb85-46d5-8406-5e8dc4a88b41')
-        self.assertIsNone(pset)
+    def test_find_nonexistent_dir(self):
+        with self.assertRaises(bt2.Error):
+            bt2.find_plugins('/this/does/not/exist/246703df-cb85-46d5-8406-5e8dc4a88b41')
+
+    def test_find_none_existing_dir(self):
+        plugins = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH, recurse=False)
+        self.assertIsNone(plugins)
 
     def test_find_dir(self):
         pset = bt2.find_plugins(_TEST_PLUGIN_PLUGINS_PATH)
index a22d2b27017f433c9c2d88d7578faef08ec90513..0bee0447052d3ef0d0e89551d6521dc8a0cc7478 100644 (file)
@@ -24,7 +24,7 @@
 #include "tap/tap.h"
 #include "common.h"
 
-#define NR_TESTS               35
+#define NR_TESTS               38
 #define NON_EXISTING_PATH      "/this/hopefully/does/not/exist/5bc75f8d-0dba-4043-a509-d7984b97e42b.so"
 
 /* Those symbols are written to by some test plugins */
@@ -64,17 +64,21 @@ static char *get_test_plugin_path(const char *plugin_dir,
 
 static void test_minimal(const char *plugin_dir)
 {
-       const bt_plugin_set *plugin_set;
+       const bt_plugin_set *plugin_set = NULL;
        const bt_plugin *plugin;
        char *minimal_path = get_test_plugin_path(plugin_dir, "minimal");
+       bt_plugin_status status;
 
        BT_ASSERT(minimal_path);
        diag("minimal plugin test below");
 
        reset_test_plugin_env_vars();
-       plugin_set = bt_plugin_find_all_from_file(minimal_path);
-       ok(plugin_set && bt_plugin_set_get_plugin_count(plugin_set) == 1,
+       status = bt_plugin_find_all_from_file(minimal_path, BT_FALSE,
+               &plugin_set);
+       ok(status == BT_PLUGIN_STATUS_OK,
                "bt_plugin_find_all_from_file() succeeds with a valid file");
+       ok(plugin_set,
+               "bt_plugin_find_all_from_file() returns a plugin set");
        ok(check_env_var("BT_TEST_PLUGIN_INIT_CALLED") == 1,
                "plugin's initialization function is called during bt_plugin_find_all_from_file()");
        ok(bt_plugin_set_get_plugin_count(plugin_set) == 1,
@@ -109,7 +113,7 @@ static void test_minimal(const char *plugin_dir)
 
 static void test_sfs(const char *plugin_dir)
 {
-       const bt_plugin_set *plugin_set;
+       const bt_plugin_set *plugin_set = NULL;
        const bt_plugin *plugin;
        const bt_component_class_sink *sink_comp_class;
        const bt_component_class_source *source_comp_class;
@@ -127,13 +131,15 @@ static void test_sfs(const char *plugin_dir)
        bt_graph_status graph_ret;
        bt_query_executor *query_exec = bt_query_executor_create();
        int ret;
+       bt_plugin_status status;
 
        BT_ASSERT(query_exec);
        BT_ASSERT(sfs_path);
        diag("sfs plugin test below");
 
-       plugin_set = bt_plugin_find_all_from_file(sfs_path);
-       BT_ASSERT(plugin_set && bt_plugin_set_get_plugin_count(plugin_set) == 1);
+       status = bt_plugin_find_all_from_file(sfs_path, BT_FALSE, &plugin_set);
+       BT_ASSERT(status == BT_PLUGIN_STATUS_OK && plugin_set &&
+               bt_plugin_set_get_plugin_count(plugin_set) == 1);
        plugin = bt_plugin_set_borrow_plugin_by_index_const(plugin_set, 0);
        ok(bt_plugin_get_version(plugin, &major, &minor, &patch, &extra) ==
                BT_PROPERTY_AVAILABILITY_AVAILABLE,
@@ -211,15 +217,22 @@ static void test_sfs(const char *plugin_dir)
 static void test_create_all_from_dir(const char *plugin_dir)
 {
        const bt_plugin_set *plugin_set;
+       bt_plugin_status status;
 
        diag("create from all test below");
 
-       plugin_set = bt_plugin_find_all_from_dir(NON_EXISTING_PATH, BT_FALSE);
-       ok(!plugin_set,
+       status = bt_plugin_find_all_from_dir(NON_EXISTING_PATH, BT_FALSE,
+               BT_FALSE, &plugin_set);
+       ok(status == BT_PLUGIN_STATUS_ERROR,
                "bt_plugin_find_all_from_dir() fails with an invalid path");
 
-       plugin_set = bt_plugin_find_all_from_dir(plugin_dir, BT_FALSE);
-       ok(plugin_set, "bt_plugin_find_all_from_dir() succeeds with a valid path");
+       plugin_set = NULL;
+       status = bt_plugin_find_all_from_dir(plugin_dir, BT_FALSE, BT_FALSE,
+               &plugin_set);
+       ok(status == BT_PLUGIN_STATUS_OK,
+               "bt_plugin_find_all_from_dir() succeeds with a valid path");
+       ok(plugin_set,
+               "bt_plugin_find_all_from_dir() returns a plugin set with a valid path");
 
        /* 2 or 4, if `.la` files are considered or not */
        ok(bt_plugin_set_get_plugin_count(plugin_set) == 2 ||
@@ -234,9 +247,11 @@ static void test_find(const char *plugin_dir)
        int ret;
        const bt_plugin *plugin;
        char *plugin_path;
+       bt_plugin_status status;
 
-       ok(!bt_plugin_find(NON_EXISTING_PATH),
-               "bt_plugin_find() returns NULL with an unknown plugin name");
+       ok(bt_plugin_find(NON_EXISTING_PATH, BT_FALSE, &plugin) ==
+               BT_PLUGIN_STATUS_NOT_FOUND,
+               "bt_plugin_find() returns BT_PLUGIN_STATUS_NOT_FOUND with an unknown plugin name");
        ret = asprintf(&plugin_path, "%s" G_SEARCHPATH_SEPARATOR_S
                        G_DIR_SEPARATOR_S "ec1d09e5-696c-442e-b1c3-f9c6cf7f5958"
                        G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S
@@ -246,9 +261,11 @@ static void test_find(const char *plugin_dir)
                NON_EXISTING_PATH, plugin_dir);
        BT_ASSERT(ret > 0 && plugin_path);
        g_setenv("BABELTRACE_PLUGIN_PATH", plugin_path, 1);
-       plugin = bt_plugin_find("test_minimal");
-       ok(plugin,
+       plugin = NULL;
+       status = bt_plugin_find("test_minimal", BT_FALSE, &plugin);
+       ok(status == BT_PLUGIN_STATUS_OK,
                "bt_plugin_find() succeeds with a plugin name it can find");
+       ok(plugin, "bt_plugin_find() returns a plugin object");
        ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0,
                "bt_plugin_find() finds the correct plugin for a given name");
        BT_PLUGIN_PUT_REF_AND_RESET(plugin);
This page took 0.059796 seconds and 4 git commands to generate.