GCC Code Coverage Report


Directory: src/
File: src/talp/backend_manager.c
Date: 2026-09-15 07:37:49
Exec Total Coverage
Lines: 37 106 34.9%
Functions: 5 9 55.6%
Branches: 21 76 27.6%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2026 Barcelona Supercomputing Center */
3 /* */
4 /* This file is part of the DLB library. */
5 /* */
6 /* DLB is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU Lesser General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* DLB is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU Lesser General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU Lesser General Public License */
17 /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18 /*********************************************************************************/
19
20 #include "talp/backend_manager.h"
21
22 #include "apis/dlb_errors.h"
23 #include "support/debug.h"
24 #include "talp/backend.h"
25 #include "talp/talp_gpu.h"
26 #include "talp/talp_hwc.h"
27
28 #include <dirent.h>
29 #include <dlfcn.h>
30 #include <libgen.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35
36 // Internal Core interface that plugins may use
37 const core_api_t core_api = {
38 .abi_version = DLB_BACKEND_ABI_VERSION,
39 .struct_size = sizeof(core_api_t),
40 .gpu = {
41 .enter_runtime = talp_gpu_enter_runtime,
42 .exit_runtime = talp_gpu_exit_runtime,
43 },
44 };
45
46 // Loaded plugins. We only accept one plugin per capability
47 typedef struct plugin_t {
48 const backend_api_t *api;
49 void *handle;
50 } plugin_t;
51
52
53 /* --- GPU plguins ------------------------------------------------------------ */
54
55 // List of possible GPU backend plugin names
56 static const char *gpu_plugins[] = {
57 "cupti",
58 "rocprofiler-sdk",
59 "rocprofilerv2",
60 };
61
62 enum { num_gpu_plugins = sizeof(gpu_plugins) / sizeof(gpu_plugins[0]) };
63
64 static plugin_t loaded_gpu_plugin = {};
65
66
67 /* --- HWC plguins ------------------------------------------------------------ */
68
69 // List of possible HWC backend plugin names
70 static const char *hwc_plugins[] = {
71 "papi",
72 };
73
74 enum { num_hwc_plugins = sizeof(hwc_plugins) / sizeof(hwc_plugins[0]) };
75
76 static plugin_t loaded_hwc_plugin = {};
77
78
79 /* ----------------------------------------------------------------------------- */
80
81 /* Obtain full path to libdlb.so (or whatever variant is loaded) */
82 15 static const char* get_dlb_lib_path(void) {
83
84 static char dlb_lib_path[PATH_MAX] = "";
85
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (dlb_lib_path[0] != '\0') return dlb_lib_path;
87
88 // Get information of the dlb shared object currently loaded
89 Dl_info info;
90
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 if (dladdr((void*)__func__, &info)) {
91 15 snprintf(dlb_lib_path, sizeof(dlb_lib_path), "%s", dirname((char*)info.dli_fname));
92 } else {
93 debug_warning("dladdr failed to obtain information");
94 return NULL;
95 }
96
97 15 return dlb_lib_path;
98 }
99
100 /* Obtain DLB_LIB_DIR environment variable */
101 88 static const char* get_dlb_plugin_path(void) {
102
103 static char dlb_plugin_path[PATH_MAX] = "";
104
105
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 15 times.
88 if (dlb_plugin_path[0] != '\0') return dlb_plugin_path;
106
107 // Try env var first
108 15 const char *dlb_plugin_path_env = getenv("DLB_LIB_DIR");
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (dlb_plugin_path_env != NULL) {
110 DIR* dir = opendir(dlb_plugin_path_env);
111 if (dir) {
112 snprintf(dlb_plugin_path, sizeof(dlb_plugin_path), "%s", dlb_plugin_path_env);
113 closedir(dir);
114 } else {
115 dlb_plugin_path_env = NULL;
116 }
117 }
118
119 // Try same directoy as dlb library
120
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (dlb_plugin_path_env == NULL) {
121 15 snprintf(dlb_plugin_path, sizeof(dlb_plugin_path), "%s", get_dlb_lib_path());
122 }
123
124 15 return dlb_plugin_path;
125 }
126
127 /* Load exact plugin name, return error otherwise */
128 88 static int talp_backend_manager_load(const char* plugin_name) {
129
130 88 const char *dlb_plugin_path = get_dlb_plugin_path();
131
2/4
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
88 if (dlb_plugin_path == NULL || dlb_plugin_path[0] == '\0') {
132 return DLB_ERR_UNKNOWN;
133 }
134
135 #ifndef DLB_TEST_LIB
136 88 const char *plugin_suffix = "";
137 88 int dlopen_flag = RTLD_LAZY;
138 #else
139 /* stub libraries contain symbols that will be called during testing,
140 * so we'll load the plugin and all symbols from its linked librarties. */
141 const char *plugin_suffix = "_test";
142 int dlopen_flag = RTLD_NOW | RTLD_GLOBAL;
143 #endif
144
145 // Load the plugin
146 char plugin_path[PATH_MAX];
147 88 int not_truncated_len = snprintf(plugin_path, PATH_MAX, "%s/libdlb_%s%s.so",
148 dlb_plugin_path, plugin_name, plugin_suffix);
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (not_truncated_len >= PATH_MAX) {
150 // really unlikely, but avoids GCC's format-truncation warning
151 return DLB_ERR_UNKNOWN;
152 }
153 88 void *handle = dlopen(plugin_path, dlopen_flag);
154
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if (handle == NULL) {
155 88 debug_warning("Failed to load plugin %s: %s", plugin_name, dlerror());
156 88 return DLB_ERR_UNKNOWN;
157 }
158
159 // Load the plugin public function
160 typedef backend_api_t* (*backend_get_api_func_t)(void);
161 backend_get_api_func_t get_api = (backend_get_api_func_t)dlsym(handle, "DLB_Get_Backend_API");
162 if (get_api == NULL) {
163 debug_warning("Failed to find function: %s", dlerror());
164 goto close_handle_and_error;
165 }
166
167 // Check that DLB_Get_Backend_API returns a valid API
168 const backend_api_t *backend_api = get_api();
169 if (backend_api == NULL) {
170 debug_warning("DLB_Get_Backend_API returned NULL in plugin %s", plugin_name);
171 goto close_handle_and_error;
172 }
173
174 // Check ABI version
175 if (backend_api->abi_version != DLB_BACKEND_ABI_VERSION
176 || backend_api->struct_size != sizeof(backend_api_t)) {
177 debug_warning("ABI compatibility check failed in plugin %s", plugin_name);
178 goto close_handle_and_error;
179 }
180
181 // Check that plugin return its own name
182 if (strcmp(backend_api->name, plugin_name) != 0) {
183 debug_warning("Plugin %s did not return an equivalent plugin name during initialization."
184 " Deactivating...", plugin_name);
185 goto close_handle_and_error;
186 }
187
188 // Check plugin capabilities
189 if (backend_api->capabilities.gpu) {
190 loaded_gpu_plugin.api = backend_api;
191 loaded_gpu_plugin.handle = handle;
192 } else if (backend_api->capabilities.hwc) {
193 loaded_hwc_plugin.api = backend_api;
194 loaded_hwc_plugin.handle = handle;
195 } else {
196 debug_warning("Unkown capabilities for plugin %s", plugin_name);
197 goto close_handle_and_error;
198 }
199
200 return DLB_SUCCESS;
201
202 close_handle_and_error:
203 dlclose(handle);
204 return DLB_ERR_UNKNOWN;
205 }
206
207 20 const backend_api_t* talp_backend_manager_load_gpu_backend(const char *name) {
208
209
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (loaded_gpu_plugin.handle == NULL) {
210
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 if (name == NULL || name[0] == '\0') {
211 // Try all possible GPU plugins until one succeeds
212
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 20 times.
80 for (size_t i = 0; i < num_gpu_plugins; ++i) {
213 60 const char *p = gpu_plugins[i];
214
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
60 if (talp_backend_manager_load(p) == DLB_SUCCESS) {
215 break;
216 }
217 }
218 } else {
219 talp_backend_manager_load(name);
220 }
221 }
222
223 // Note that it may be NULL if no GPU plugin could be loaded
224 20 return loaded_gpu_plugin.api;
225 }
226
227 void talp_backend_manager_unload_gpu_backend(void) {
228
229 if (loaded_gpu_plugin.handle != NULL) {
230 dlclose(loaded_gpu_plugin.handle);
231 loaded_gpu_plugin = (const plugin_t){};
232 }
233 }
234
235 28 const backend_api_t* talp_backend_manager_load_hwc_backend(const char *name) {
236
237
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (loaded_hwc_plugin.handle == NULL) {
238
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
28 if (name == NULL || name[0] == '\0') {
239 // Try all possible HWC plugins until one succeeds
240
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 for (size_t i = 0; i < num_hwc_plugins; ++i) {
241 28 const char *p = hwc_plugins[i];
242
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 if (talp_backend_manager_load(p) == DLB_SUCCESS) {
243 break;
244 }
245 }
246 } else {
247 talp_backend_manager_load(name);
248 }
249 }
250
251 // Note that it may be NULL if no HWC plugin could be loaded
252 28 return loaded_hwc_plugin.api;
253 }
254
255 void talp_backend_manager_unload_hwc_backend(void) {
256
257 if (loaded_hwc_plugin.handle != NULL) {
258 dlclose(loaded_hwc_plugin.handle);
259 loaded_hwc_plugin = (const plugin_t){};
260 }
261 }
262
263 void* talp_backend_manager_get_symbol_from_plugin(const char *symbol, const char *plugin_name) {
264
265 if (loaded_gpu_plugin.handle != NULL
266 && strcmp(loaded_gpu_plugin.api->name, plugin_name) == 0) {
267 return dlsym(loaded_gpu_plugin.handle, symbol);
268 }
269
270 if (loaded_hwc_plugin.handle != NULL
271 && strcmp(loaded_hwc_plugin.api->name, plugin_name) == 0) {
272 return dlsym(loaded_hwc_plugin.handle, symbol);
273 }
274
275 return NULL;
276 }
277
278 int talp_backend_manager_get_gpu_affinity(char *buffer, size_t buffer_size, bool full_uuid) {
279
280 // This function is only called from `dlb` utilility, so we don't expect
281 // having the plugin already loaded, but we can support it just in case
282 bool plugin_needs_loading = loaded_gpu_plugin.handle == NULL;
283
284 if (plugin_needs_loading) {
285 talp_backend_manager_load_gpu_backend(NULL);
286 }
287
288 int error = DLB_ERR_UNKNOWN;
289 if (loaded_gpu_plugin.handle != NULL
290 && loaded_gpu_plugin.api->gpu.get_uuids) {
291 int backend_error = loaded_gpu_plugin.api->gpu.get_uuids(buffer, buffer_size, full_uuid);
292 if (backend_error == DLB_BACKEND_SUCCESS) {
293 error = DLB_SUCCESS;
294 }
295 }
296
297 if (plugin_needs_loading) {
298 talp_backend_manager_unload_gpu_backend();
299 }
300
301 return error;
302 }
303