GCC Code Coverage Report


Directory: src/
File: src/talp/backend_manager.c
Date: 2026-10-14 08:39:21
Exec Total Coverage
Lines: 37 106 34.9%
Functions: 5 9 55.6%
Branches: 22 76 28.9%

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 "perf",
73 };
74
75 enum { num_hwc_plugins = sizeof(hwc_plugins) / sizeof(hwc_plugins[0]) };
76
77 static plugin_t loaded_hwc_plugin = {};
78
79
80 /* ----------------------------------------------------------------------------- */
81
82 /* Obtain full path to libdlb.so (or whatever variant is loaded) */
83 15 static const char* get_dlb_lib_path(void) {
84
85 static char dlb_lib_path[PATH_MAX] = "";
86
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (dlb_lib_path[0] != '\0') return dlb_lib_path;
88
89 // Get information of the dlb shared object currently loaded
90 Dl_info info;
91
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 if (dladdr((void*)__func__, &info)) {
92 15 snprintf(dlb_lib_path, sizeof(dlb_lib_path), "%s", dirname((char*)info.dli_fname));
93 } else {
94 ✗ debug_warning("dladdr failed to obtain information");
95 ✗ return NULL;
96 }
97
98 15 return dlb_lib_path;
99 }
100
101 /* Obtain DLB_LIB_DIR environment variable */
102 116 static const char* get_dlb_plugin_path(void) {
103
104 static char dlb_plugin_path[PATH_MAX] = "";
105
106
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 15 times.
116 if (dlb_plugin_path[0] != '\0') return dlb_plugin_path;
107
108 // Try env var first
109 15 const char *dlb_plugin_path_env = getenv("DLB_LIB_DIR");
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (dlb_plugin_path_env != NULL) {
111 ✗ DIR* dir = opendir(dlb_plugin_path_env);
112 ✗ if (dir) {
113 ✗ snprintf(dlb_plugin_path, sizeof(dlb_plugin_path), "%s", dlb_plugin_path_env);
114 ✗ closedir(dir);
115 } else {
116 ✗ dlb_plugin_path_env = NULL;
117 }
118 }
119
120 // Try same directoy as dlb library
121
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (dlb_plugin_path_env == NULL) {
122 15 snprintf(dlb_plugin_path, sizeof(dlb_plugin_path), "%s", get_dlb_lib_path());
123 }
124
125 15 return dlb_plugin_path;
126 }
127
128 /* Load exact plugin name, return error otherwise */
129 116 static int talp_backend_manager_load(const char* plugin_name) {
130
131 116 const char *dlb_plugin_path = get_dlb_plugin_path();
132
2/4
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 116 times.
116 if (dlb_plugin_path == NULL || dlb_plugin_path[0] == '\0') {
133 ✗ return DLB_ERR_UNKNOWN;
134 }
135
136 #ifndef DLB_TEST_LIB
137 116 const char *plugin_suffix = "";
138 116 int dlopen_flag = RTLD_LAZY;
139 #else
140 /* stub libraries contain symbols that will be called during testing,
141 * so we'll load the plugin and all symbols from its linked librarties. */
142 const char *plugin_suffix = "_test";
143 int dlopen_flag = RTLD_NOW | RTLD_GLOBAL;
144 #endif
145
146 // Load the plugin
147 char plugin_path[PATH_MAX];
148 116 int not_truncated_len = snprintf(plugin_path, PATH_MAX, "%s/libdlb_%s%s.so",
149 dlb_plugin_path, plugin_name, plugin_suffix);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 if (not_truncated_len >= PATH_MAX) {
151 // really unlikely, but avoids GCC's format-truncation warning
152 ✗ return DLB_ERR_UNKNOWN;
153 }
154 116 void *handle = dlopen(plugin_path, dlopen_flag);
155
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 if (handle == NULL) {
156 116 debug_warning("Failed to load plugin %s: %s", plugin_name, dlerror());
157 116 return DLB_ERR_UNKNOWN;
158 }
159
160 // Load the plugin public function
161 typedef backend_api_t* (*backend_get_api_func_t)(void);
162 ✗ backend_get_api_func_t get_api = (backend_get_api_func_t)dlsym(handle, "DLB_Get_Backend_API");
163 ✗ if (get_api == NULL) {
164 ✗ debug_warning("Failed to find function: %s", dlerror());
165 ✗ goto close_handle_and_error;
166 }
167
168 // Check that DLB_Get_Backend_API returns a valid API
169 ✗ const backend_api_t *backend_api = get_api();
170 ✗ if (backend_api == NULL) {
171 ✗ debug_warning("DLB_Get_Backend_API returned NULL in plugin %s", plugin_name);
172 ✗ goto close_handle_and_error;
173 }
174
175 // Check ABI version
176 ✗ if (backend_api->abi_version != DLB_BACKEND_ABI_VERSION
177 ✗ || backend_api->struct_size != sizeof(backend_api_t)) {
178 ✗ debug_warning("ABI compatibility check failed in plugin %s", plugin_name);
179 ✗ goto close_handle_and_error;
180 }
181
182 // Check that plugin return its own name
183 ✗ if (strcmp(backend_api->name, plugin_name) != 0) {
184 ✗ debug_warning("Plugin %s did not return an equivalent plugin name during initialization."
185 " Deactivating...", plugin_name);
186 ✗ goto close_handle_and_error;
187 }
188
189 // Check plugin capabilities
190 ✗ if (backend_api->capabilities.gpu) {
191 ✗ loaded_gpu_plugin.api = backend_api;
192 ✗ loaded_gpu_plugin.handle = handle;
193 ✗ } else if (backend_api->capabilities.hwc) {
194 ✗ loaded_hwc_plugin.api = backend_api;
195 ✗ loaded_hwc_plugin.handle = handle;
196 } else {
197 ✗ debug_warning("Unkown capabilities for plugin %s", plugin_name);
198 ✗ goto close_handle_and_error;
199 }
200
201 ✗ return DLB_SUCCESS;
202
203 ✗ close_handle_and_error:
204 ✗ dlclose(handle);
205 ✗ return DLB_ERR_UNKNOWN;
206 }
207
208 20 const backend_api_t* talp_backend_manager_load_gpu_backend(const char *name) {
209
210
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (loaded_gpu_plugin.handle == NULL) {
211
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') {
212 // Try all possible GPU plugins until one succeeds
213
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 20 times.
80 for (size_t i = 0; i < num_gpu_plugins; ++i) {
214 60 const char *p = gpu_plugins[i];
215
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
60 if (talp_backend_manager_load(p) == DLB_SUCCESS) {
216 ✗ break;
217 }
218 }
219 } else {
220 ✗ talp_backend_manager_load(name);
221 }
222 }
223
224 // Note that it may be NULL if no GPU plugin could be loaded
225 20 return loaded_gpu_plugin.api;
226 }
227
228 ✗ void talp_backend_manager_unload_gpu_backend(void) {
229
230 ✗ if (loaded_gpu_plugin.handle != NULL) {
231 ✗ dlclose(loaded_gpu_plugin.handle);
232 ✗ loaded_gpu_plugin = (const plugin_t){};
233 }
234 }
235
236 28 const backend_api_t* talp_backend_manager_load_hwc_backend(const char *name) {
237
238
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (loaded_hwc_plugin.handle == NULL) {
239
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 if (name == NULL || name[0] == '\0') {
240 // Try all possible HWC plugins until one succeeds
241
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 28 times.
84 for (size_t i = 0; i < num_hwc_plugins; ++i) {
242 56 const char *p = hwc_plugins[i];
243
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if (talp_backend_manager_load(p) == DLB_SUCCESS) {
244 ✗ break;
245 }
246 }
247 } else {
248 ✗ talp_backend_manager_load(name);
249 }
250 }
251
252 // Note that it may be NULL if no HWC plugin could be loaded
253 28 return loaded_hwc_plugin.api;
254 }
255
256 ✗ void talp_backend_manager_unload_hwc_backend(void) {
257
258 ✗ if (loaded_hwc_plugin.handle != NULL) {
259 ✗ dlclose(loaded_hwc_plugin.handle);
260 ✗ loaded_hwc_plugin = (const plugin_t){};
261 }
262 }
263
264 ✗ void* talp_backend_manager_get_symbol_from_plugin(const char *symbol, const char *plugin_name) {
265
266 ✗ if (loaded_gpu_plugin.handle != NULL
267 ✗ && strcmp(loaded_gpu_plugin.api->name, plugin_name) == 0) {
268 ✗ return dlsym(loaded_gpu_plugin.handle, symbol);
269 }
270
271 ✗ if (loaded_hwc_plugin.handle != NULL
272 ✗ && strcmp(loaded_hwc_plugin.api->name, plugin_name) == 0) {
273 ✗ return dlsym(loaded_hwc_plugin.handle, symbol);
274 }
275
276 ✗ return NULL;
277 }
278
279 ✗ int talp_backend_manager_get_gpu_affinity(char *buffer, size_t buffer_size, bool full_uuid) {
280
281 // This function is only called from `dlb` utilility, so we don't expect
282 // having the plugin already loaded, but we can support it just in case
283 ✗ bool plugin_needs_loading = loaded_gpu_plugin.handle == NULL;
284
285 ✗ if (plugin_needs_loading) {
286 ✗ talp_backend_manager_load_gpu_backend(NULL);
287 }
288
289 ✗ int error = DLB_ERR_UNKNOWN;
290 ✗ if (loaded_gpu_plugin.handle != NULL
291 ✗ && loaded_gpu_plugin.api->gpu.get_uuids) {
292 ✗ int backend_error = loaded_gpu_plugin.api->gpu.get_uuids(buffer, buffer_size, full_uuid);
293 ✗ if (backend_error == DLB_BACKEND_SUCCESS) {
294 ✗ error = DLB_SUCCESS;
295 }
296 }
297
298 ✗ if (plugin_needs_loading) {
299 ✗ talp_backend_manager_unload_gpu_backend();
300 }
301
302 ✗ return error;
303 }
304