6 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
9 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 #include <babeltrace/compiler.h>
31 #include <babeltrace/ref.h>
32 #include <babeltrace/plugin/plugin-dev.h>
33 #include <babeltrace/plugin/plugin-internal.h>
34 #include <babeltrace/component/component-class-internal.h>
44 #define PLUGIN_SYMBOL_NAME "__bt_plugin_name"
45 #define PLUGIN_SYMBOL_AUTHOR "__bt_plugin_author"
46 #define PLUGIN_SYMBOL_LICENSE "__bt_plugin_license"
47 #define PLUGIN_SYMBOL_INIT "__bt_plugin_init"
48 #define PLUGIN_SYMBOL_EXIT "__bt_plugin_exit"
49 #define PLUGIN_SYMBOL_DESCRIPTION "__bt_plugin_description"
50 #define NATIVE_PLUGIN_SUFFIX ".so"
51 #define NATIVE_PLUGIN_SUFFIX_LEN sizeof(NATIVE_PLUGIN_SUFFIX)
52 #define LIBTOOL_PLUGIN_SUFFIX ".la"
53 #define LIBTOOL_PLUGIN_SUFFIX_LEN sizeof(LIBTOOL_PLUGIN_SUFFIX)
55 #define PLUGIN_SUFFIX_LEN max_t(size_t, sizeof(NATIVE_PLUGIN_SUFFIX), \
56 sizeof(LIBTOOL_PLUGIN_SUFFIX))
58 #define SECTION_BEGIN(_name) &__start_##_name
59 #define SECTION_END(_name) &__stop_##_name
60 #define SECTION_ELEMENT_COUNT(_name) (SECTION_END(_name) - SECTION_BEGIN(_name))
62 #define DECLARE_SECTION(_type, _name) \
63 extern _type const __start_##_name __attribute((weak)); \
64 extern _type const __stop_##_name __attribute((weak))
66 DECLARE_SECTION(bt_plugin_init_func
, __bt_plugin_init_funcs
);
67 DECLARE_SECTION(bt_plugin_exit_func
, __bt_plugin_exit_funcs
);
68 DECLARE_SECTION(const char *, __bt_plugin_names
);
69 DECLARE_SECTION(const char *, __bt_plugin_authors
);
70 DECLARE_SECTION(const char *, __bt_plugin_licenses
);
71 DECLARE_SECTION(const char *, __bt_plugin_descriptions
);
73 #define PRINT_SECTION(_printer, _name) \
75 _printer("Section " #_name " [%p - %p], (%zu elements)\n", \
76 SECTION_BEGIN(_name), SECTION_END(_name), \
77 SECTION_ELEMENT_COUNT(_name)); \
80 #define PRINT_PLUG_IN_SECTIONS(_printer) \
82 PRINT_SECTION(_printer, __bt_plugin_init_funcs); \
83 PRINT_SECTION(_printer, __bt_plugin_exit_funcs); \
84 PRINT_SECTION(_printer, __bt_plugin_names); \
85 PRINT_SECTION(_printer, __bt_plugin_authors); \
86 PRINT_SECTION(_printer, __bt_plugin_licenses); \
87 PRINT_SECTION(_printer, __bt_plugin_descriptions); \
91 * This hash table, global to the library, maps component class pointers
92 * to shared library handles.
94 * The keys (component classes) are NOT owned by this hash table, whereas
95 * the values (shared library handles) are owned by this hash table.
97 * The keys are the component classes created with
98 * bt_plugin_add_component_class(). They keep the shared library handle
99 * object created by their plugin alive so that the plugin's code is
100 * not discarded when it could still be in use by living components
101 * created from those component classes:
103 * [component] --ref-> [component class] --through this HT-> [shlib handle]
105 * This hash table exists for two reasons:
107 * 1. To allow this application:
109 * my_plugin = bt_plugin_create_from_file("/path/to/my-plugin.so");
110 * // instantiate components from the plugin's component classes
112 * // user code of instantiated components still exists
114 * 2. To decouple the plugin subsystem from the component subsystem:
115 * while plugins objects need to know component class objects, the
116 * opposite is not necessary, thus it makes no sense for a component
117 * class to keep a reference to the plugin object from which it was
120 * An entry is removed from this HT when a component class is destroyed
121 * thanks to a custom destroy listener. When the entry is removed, the
122 * GLib function calls the value destroy notifier of the HT, which is
123 * bt_put(). This decreases the reference count of the mapped shared
124 * library handle. Assuming the original plugin object which contained
125 * some component classes is put first, when the last component class is
126 * removed from this HT, the shared library handle object's reference
127 * count falls to zero and the shared library is finally closed.
130 GHashTable
*comp_classes_to_shlib_handles
;
132 __attribute__((constructor
)) static
133 void init_comp_classes_to_shlib_handles(void) {
134 comp_classes_to_shlib_handles
= g_hash_table_new_full(g_direct_hash
,
135 g_direct_equal
, NULL
, bt_put
);
136 assert(comp_classes_to_shlib_handles
);
139 __attribute__((destructor
)) static
140 void fini_comp_classes_to_shlib_handles(void) {
141 if (comp_classes_to_shlib_handles
) {
142 g_hash_table_destroy(comp_classes_to_shlib_handles
);
147 void bt_plugin_shared_lib_handle_destroy(struct bt_object
*obj
)
149 struct bt_plugin_shared_lib_handle
*shared_lib_handle
;
152 shared_lib_handle
= container_of(obj
,
153 struct bt_plugin_shared_lib_handle
, base
);
155 if (shared_lib_handle
->init_called
&& shared_lib_handle
->exit
) {
156 enum bt_plugin_status status
= shared_lib_handle
->exit();
159 printf_verbose("Plugin `%s` exited with error %d\n",
160 shared_lib_handle
->name
, status
);
164 if (shared_lib_handle
->module
) {
165 if (!g_module_close(shared_lib_handle
->module
)) {
166 printf_error("Module close error: %s\n",
171 if (shared_lib_handle
->path
) {
172 g_string_free(shared_lib_handle
->path
, TRUE
);
175 g_free(shared_lib_handle
);
179 struct bt_plugin_shared_lib_handle
*bt_plugin_shared_lib_handle_create(
182 struct bt_plugin_shared_lib_handle
*shared_lib_handle
= NULL
;
183 gpointer symbol
= NULL
;
185 shared_lib_handle
= g_new0(struct bt_plugin_shared_lib_handle
, 1);
186 if (!shared_lib_handle
) {
190 bt_object_init(shared_lib_handle
, bt_plugin_shared_lib_handle_destroy
);
196 shared_lib_handle
->path
= g_string_new(path
);
197 if (!shared_lib_handle
->path
) {
201 shared_lib_handle
->module
= g_module_open(path
, 0);
202 if (!shared_lib_handle
->module
) {
203 printf_verbose("Module open error: %s\n", g_module_error());
207 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_NAME
,
208 (gpointer
*) &shared_lib_handle
->name
)) {
209 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
211 g_module_name(shared_lib_handle
->module
));
215 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_LICENSE
,
216 (gpointer
*) &shared_lib_handle
->license
)) {
217 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
218 PLUGIN_SYMBOL_LICENSE
,
219 g_module_name(shared_lib_handle
->module
));
223 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_AUTHOR
,
224 (gpointer
*) &shared_lib_handle
->author
)) {
225 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
226 PLUGIN_SYMBOL_AUTHOR
,
227 g_module_name(shared_lib_handle
->module
));
231 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_DESCRIPTION
,
232 (gpointer
*) &shared_lib_handle
->description
)) {
233 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
234 PLUGIN_SYMBOL_DESCRIPTION
,
235 g_module_name(shared_lib_handle
->module
));
239 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_INIT
,
241 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
243 g_module_name(shared_lib_handle
->module
));
246 shared_lib_handle
->init
= *((bt_plugin_init_func
*) symbol
);
247 if (!shared_lib_handle
->init
) {
248 printf_verbose("NULL %s symbol target\n",
254 if (!g_module_symbol(shared_lib_handle
->module
, PLUGIN_SYMBOL_EXIT
,
256 printf_verbose("Unable to resolve plugin symbol %s from %s\n",
258 g_module_name(shared_lib_handle
->module
));
261 shared_lib_handle
->exit
= *((bt_plugin_exit_func
*) symbol
);
262 if (!shared_lib_handle
->exit
) {
263 printf_verbose("NULL %s symbol target\n",
272 BT_PUT(shared_lib_handle
);
275 return shared_lib_handle
;
279 void bt_plugin_destroy(struct bt_object
*obj
)
281 struct bt_plugin
*plugin
;
284 plugin
= container_of(obj
, struct bt_plugin
, base
);
286 BT_PUT(plugin
->shared_lib_handle
);
288 if (plugin
->comp_classes
) {
289 g_ptr_array_free(plugin
->comp_classes
, TRUE
);
296 enum bt_plugin_status
init_plugin(struct bt_plugin
*plugin
)
298 enum bt_plugin_status status
= BT_PLUGIN_STATUS_OK
;
300 if (plugin
->shared_lib_handle
->init
) {
301 status
= plugin
->shared_lib_handle
->init(plugin
);
304 printf_verbose("Plugin `%s` initialization error: %d\n",
305 plugin
->shared_lib_handle
->name
, status
);
310 plugin
->shared_lib_handle
->init_called
= true;
313 * The initialization function should have added the component
314 * classes at this point. We freeze the plugin so that it's not
315 * possible to add component classes to this plugin object after
316 * this stage (plugin object becomes immutable).
318 plugin
->frozen
= true;
324 struct bt_plugin
*bt_plugin_create_from_file(const char *path
)
327 struct bt_plugin
*plugin
= NULL
;
328 bool is_libtool_wrapper
= false, is_shared_object
= false;
334 path_len
= strlen(path
);
335 if (path_len
<= PLUGIN_SUFFIX_LEN
) {
341 * Check if the file ends with a known plugin file type suffix (i.e. .so
344 is_libtool_wrapper
= !strncmp(LIBTOOL_PLUGIN_SUFFIX
,
345 path
+ path_len
- LIBTOOL_PLUGIN_SUFFIX_LEN
,
346 LIBTOOL_PLUGIN_SUFFIX_LEN
);
347 is_shared_object
= !strncmp(NATIVE_PLUGIN_SUFFIX
,
348 path
+ path_len
- NATIVE_PLUGIN_SUFFIX_LEN
,
349 NATIVE_PLUGIN_SUFFIX_LEN
);
350 if (!is_shared_object
&& !is_libtool_wrapper
) {
351 /* Name indicates that this is not a plugin file. */
355 plugin
= g_new0(struct bt_plugin
, 1);
360 bt_object_init(plugin
, bt_plugin_destroy
);
362 /* Create shared lib handle */
363 plugin
->shared_lib_handle
= bt_plugin_shared_lib_handle_create(path
);
364 if (!plugin
->shared_lib_handle
) {
365 printf_verbose("Failed to create a shared library handle (path `%s`)\n",
370 /* Create empty array of component classes */
371 plugin
->comp_classes
=
372 g_ptr_array_new_with_free_func((GDestroyNotify
) bt_put
);
373 if (!plugin
->comp_classes
) {
377 /* Initialize plugin */
378 if (init_plugin(plugin
) < 0) {
391 /* Allocate dirent as recommended by READDIR(3), NOTES on readdir_r */
393 struct dirent
*alloc_dirent(const char *path
)
397 struct dirent
*entry
;
399 name_max
= pathconf(path
, _PC_NAME_MAX
);
400 if (name_max
== -1) {
403 len
= offsetof(struct dirent
, d_name
) + name_max
+ 1;
404 entry
= zmalloc(len
);
409 enum bt_plugin_status
bt_plugin_create_append_all_from_dir(
410 GPtrArray
*plugins
, const char *path
, bool recurse
)
412 DIR *directory
= NULL
;
413 struct dirent
*entry
= NULL
, *result
= NULL
;
414 char *file_path
= NULL
;
415 size_t path_len
= strlen(path
);
416 enum bt_plugin_status ret
= BT_PLUGIN_STATUS_OK
;
418 if (path_len
>= PATH_MAX
) {
419 ret
= BT_PLUGIN_STATUS_ERROR
;
423 entry
= alloc_dirent(path
);
425 ret
= BT_PLUGIN_STATUS_ERROR
;
429 file_path
= zmalloc(PATH_MAX
);
431 ret
= BT_PLUGIN_STATUS_NOMEM
;
435 strncpy(file_path
, path
, path_len
);
436 /* Append a trailing '/' to the path */
437 if (file_path
[path_len
- 1] != '/') {
438 file_path
[path_len
++] = '/';
441 directory
= opendir(file_path
);
443 perror("Failed to open plug-in directory");
444 ret
= BT_PLUGIN_STATUS_ERROR
;
448 /* Recursively walk directory */
449 while (!readdir_r(directory
, entry
, &result
) && result
) {
452 size_t file_name_len
;
454 if (result
->d_name
[0] == '.') {
455 /* Skip hidden files, . and .. */
459 file_name_len
= strlen(result
->d_name
);
461 if (path_len
+ file_name_len
>= PATH_MAX
) {
465 strncpy(file_path
+ path_len
, result
->d_name
, file_name_len
);
466 file_path
[path_len
+ file_name_len
] = '\0';
468 stat_ret
= stat(file_path
, &st
);
470 /* Continue to next file / directory. */
471 printf_perror("Failed to stat() plugin file\n");
475 if (S_ISDIR(st
.st_mode
) && recurse
) {
476 ret
= bt_plugin_create_append_all_from_dir(plugins
,
481 } else if (S_ISREG(st
.st_mode
)) {
482 struct bt_plugin
*plugin
= bt_plugin_create_from_file(file_path
);
485 /* Transfer ownership to array */
486 g_ptr_array_add(plugins
, plugin
);
492 if (closedir(directory
)) {
494 * We don't want to override the error since there is
497 perror("Failed to close plug-in directory");
505 struct bt_plugin
**bt_plugin_create_all_from_dir(const char *path
,
508 GPtrArray
*plugins_array
= NULL
;
509 struct bt_plugin
**plugins
= NULL
;
510 enum bt_plugin_status status
;
516 plugins_array
= g_ptr_array_new();
517 if (!plugins_array
) {
521 /* Append found plugins to array */
522 status
= bt_plugin_create_append_all_from_dir(plugins_array
, path
,
528 /* Add sentinel to array */
529 g_ptr_array_add(plugins_array
, NULL
);
530 plugins
= (struct bt_plugin
**) plugins_array
->pdata
;
535 g_ptr_array_free(plugins_array
, TRUE
);
536 plugins_array
= NULL
;
541 g_ptr_array_free(plugins_array
, FALSE
);
548 struct bt_plugin
*bt_plugin_create_from_static_at_index(size_t i
)
550 struct bt_plugin
*plugin
= NULL
;
552 plugin
= g_new0(struct bt_plugin
, 1);
557 bt_object_init(plugin
, bt_plugin_destroy
);
559 /* Create shared lib handle */
560 plugin
->shared_lib_handle
= bt_plugin_shared_lib_handle_create(NULL
);
561 if (!plugin
->shared_lib_handle
) {
565 /* Fill shared lib handle */
566 plugin
->shared_lib_handle
->init
=
567 (SECTION_BEGIN(__bt_plugin_init_funcs
))[i
];
568 if (!plugin
->shared_lib_handle
->init
) {
572 plugin
->shared_lib_handle
->exit
=
573 (SECTION_BEGIN(__bt_plugin_exit_funcs
))[i
];
574 if (!plugin
->shared_lib_handle
->exit
) {
578 plugin
->shared_lib_handle
->name
= (SECTION_BEGIN(__bt_plugin_names
))[i
];
579 plugin
->shared_lib_handle
->author
=
580 (SECTION_BEGIN(__bt_plugin_authors
))[i
];
581 plugin
->shared_lib_handle
->license
=
582 (SECTION_BEGIN(__bt_plugin_licenses
))[i
];
583 plugin
->shared_lib_handle
->description
=
584 (SECTION_BEGIN(__bt_plugin_descriptions
))[i
];
586 /* Create empty array of component classes */
587 plugin
->comp_classes
=
588 g_ptr_array_new_with_free_func((GDestroyNotify
) bt_put
);
589 if (!plugin
->comp_classes
) {
593 /* Initialize plugin */
594 if (init_plugin(plugin
) < 0) {
607 struct bt_plugin
**bt_plugin_create_all_from_static(void)
610 struct bt_plugin
**plugins
= NULL
;
612 PRINT_PLUG_IN_SECTIONS(printf_verbose
);
613 count
= SECTION_ELEMENT_COUNT(__bt_plugin_init_funcs
);
614 if (SECTION_ELEMENT_COUNT(__bt_plugin_exit_funcs
) != count
||
615 SECTION_ELEMENT_COUNT(__bt_plugin_names
) != count
||
616 SECTION_ELEMENT_COUNT(__bt_plugin_authors
) != count
||
617 SECTION_ELEMENT_COUNT(__bt_plugin_licenses
) != count
||
618 SECTION_ELEMENT_COUNT(__bt_plugin_descriptions
) != count
) {
619 printf_error("Some statically-linked plug-ins do not define all the mandatory symbols\n");
623 printf_verbose("Detected %zu statically-linked plug-ins\n", count
);
624 plugins
= g_new0(struct bt_plugin
*, count
+ 1);
629 for (i
= 0; i
< count
; i
++) {
630 struct bt_plugin
*plugin
=
631 bt_plugin_create_from_static_at_index(i
);
634 printf_error("Cannot create statically-linked plug-in at index %zu\n",
639 /* Transfer ownership to the array */
652 const char *bt_plugin_get_name(struct bt_plugin
*plugin
)
654 return plugin
? plugin
->shared_lib_handle
->name
: NULL
;
657 const char *bt_plugin_get_author(struct bt_plugin
*plugin
)
659 return plugin
? plugin
->shared_lib_handle
->author
: NULL
;
662 const char *bt_plugin_get_license(struct bt_plugin
*plugin
)
664 return plugin
? plugin
->shared_lib_handle
->license
: NULL
;
667 const char *bt_plugin_get_path(struct bt_plugin
*plugin
)
669 return (plugin
&& plugin
->shared_lib_handle
->path
) ?
670 plugin
->shared_lib_handle
->path
->str
: NULL
;
673 const char *bt_plugin_get_description(struct bt_plugin
*plugin
)
675 return plugin
? plugin
->shared_lib_handle
->description
: NULL
;
678 int bt_plugin_get_component_class_count(struct bt_plugin
*plugin
)
680 return plugin
? plugin
->comp_classes
->len
: -1;
683 struct bt_component_class
*bt_plugin_get_component_class(
684 struct bt_plugin
*plugin
, size_t index
)
686 struct bt_component_class
*comp_class
= NULL
;
688 if (!plugin
|| index
>= plugin
->comp_classes
->len
) {
692 comp_class
= g_ptr_array_index(plugin
->comp_classes
, index
);
703 struct bt_component_class
*bt_plugin_get_component_class_by_name_and_type(
704 struct bt_plugin
*plugin
, const char *name
,
705 enum bt_component_type type
)
707 struct bt_component_class
*comp_class
= NULL
;
710 if (!plugin
|| !name
) {
714 for (i
= 0; i
< plugin
->comp_classes
->len
; i
++) {
715 struct bt_component_class
*comp_class_candidate
=
716 g_ptr_array_index(plugin
->comp_classes
, i
);
717 const char *comp_class_cand_name
=
718 bt_component_class_get_name(comp_class_candidate
);
719 enum bt_component_type comp_class_cand_type
=
720 bt_component_class_get_type(comp_class_candidate
);
722 assert(comp_class_cand_name
);
723 assert(comp_class_cand_type
>= 0);
725 if (strcmp(name
, comp_class_cand_name
) == 0 &&
726 comp_class_cand_type
== type
) {
727 comp_class
= bt_get(comp_class_candidate
);
742 void plugin_comp_class_destroy_listener(struct bt_component_class
*comp_class
,
745 gboolean exists
= g_hash_table_remove(comp_classes_to_shlib_handles
,
750 enum bt_plugin_status
bt_plugin_add_component_class(
751 struct bt_plugin
*plugin
, struct bt_component_class
*comp_class
)
753 enum bt_plugin_status status
= BT_PLUGIN_STATUS_OK
;
754 struct bt_component_class
*comp_class_dup
= NULL
;
756 int comp_class_index
= -1;
758 if (!plugin
|| !comp_class
|| plugin
->frozen
) {
762 /* Check for duplicate */
763 comp_class_dup
= bt_plugin_get_component_class_by_name_and_type(plugin
,
764 bt_component_class_get_name(comp_class
),
765 bt_component_class_get_type(comp_class
));
766 if (comp_class_dup
) {
767 printf_verbose("Plugin `%s`: adding component class with existing name `%s` and type %d\n",
768 plugin
->shared_lib_handle
->name
,
769 bt_component_class_get_name(comp_class
),
770 bt_component_class_get_type(comp_class
));
774 /* Add new component class */
775 comp_class_index
= plugin
->comp_classes
->len
;
776 g_ptr_array_add(plugin
->comp_classes
, bt_get(comp_class
));
778 /* Map component class pointer to shared lib handle in global HT */
779 g_hash_table_insert(comp_classes_to_shlib_handles
, comp_class
,
780 bt_get(plugin
->shared_lib_handle
));
782 /* Add our custom destroy listener */
783 ret
= bt_component_class_add_destroy_listener(comp_class
,
784 plugin_comp_class_destroy_listener
, NULL
);
792 /* Remove entry from global hash table (if exists) */
793 g_hash_table_remove(comp_classes_to_shlib_handles
,
796 /* Remove entry from plugin's component classes (if added) */
797 if (comp_class_index
>= 0) {
798 g_ptr_array_remove_index(plugin
->comp_classes
,
802 status
= BT_PLUGIN_STATUS_ERROR
;
805 bt_put(comp_class_dup
);