Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions dttools/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ SOURCES = \
username.c \
uuid.c \
xxmalloc.c \
gpu_commons.c \
nvidia_nvml_library.c \
gpu.c \


HEADERS_PUBLIC = \
auth.h \
Expand Down Expand Up @@ -175,6 +179,9 @@ HEADERS_PUBLIC = \
timestamp.h \
unlink_recursive.h \
xxmalloc.h \
gpu_commons.h \
nvidia_nvml_library.h \
gpu.h \

LIBRARIES = libdttools.a

Expand Down
68 changes: 68 additions & 0 deletions dttools/src/gpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#include "gpu.h"
#include "debug.h"
#include "gpu_commons.h"
#include "nvidia_nvml_library.h"
#include <string.h>

struct gpu_library *gpu_lib_init()
{
struct gpu_library *gpu_lib = calloc(1, sizeof(*gpu_lib));
if (!gpu_lib) {
debug(D_DEBUG, "Out of memory allocating gpu_library");
return NULL;
}
Comment on lines +15 to +19

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpu_lib_init() leaks gpu_lib when the library isn't found (gpu_lib_path.found is false) because it returns NULL without freeing the allocation. Free gpu_lib on all failure paths before returning NULL.

Copilot uses AI. Check for mistakes.

enum gpu_vendor vendor = NVIDIA;

// TODO: write a logic to figure out the vendor
// for now stick with nvidia
gpu_lib->vendor = vendor;
struct library_search_result gpu_lib_path = find_library_by_name(vendor);
if (gpu_lib_path.found) {
if (vendor == NVIDIA) {
gpu_lib->nvidia_lib = nvml_library_open(gpu_lib_path);

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpu_lib_init() returns a non-NULL gpu_lib even if nvml_library_open() fails (nvidia_lib stays NULL). That prevents the worker from falling back to gpu_name_get() and can cause GPU name detection to silently stop working on systems where libnvidia-ml.so is present but NVML init/symbol loading fails. Return NULL (or set vendor/impl to a working fallback) when nvml_library_open() returns NULL.

Suggested change
gpu_lib->nvidia_lib = nvml_library_open(gpu_lib_path);
gpu_lib->nvidia_lib = nvml_library_open(gpu_lib_path);
if (gpu_lib->nvidia_lib == NULL) {
free(gpu_lib);
return NULL;
}

Copilot uses AI. Check for mistakes.
return gpu_lib;
} else if (vendor == AMD) {
// work around that we are not supporting the GPU for now
free(gpu_lib);
} else {
free(gpu_lib);
}
}
return NULL;
}

void gpu_lib_close(struct gpu_library *gpu_lib)
{
if (gpu_lib != NULL) {
if (gpu_lib->vendor == NVIDIA) {
// call the close function from nvidia_library
nvml_library_close(gpu_lib->nvidia_lib);
free(gpu_lib);
} else if (gpu_lib->vendor == AMD) {
// call the close function from amd_library
free(gpu_lib);

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpu_lib_close() only frees gpu_lib for NVIDIA/AMD vendors; if gpu_lib->vendor is ever extended or contains an unexpected value, this will leak. Add a default branch that frees gpu_lib (and closes any opened handles) to make destruction robust.

Suggested change
free(gpu_lib);
free(gpu_lib);
} else {
/*
* Be defensive for unexpected or future vendor values:
* close any known opened handles and always free gpu_lib.
*/
if (gpu_lib->nvidia_lib != NULL) {
nvml_library_close(gpu_lib->nvidia_lib);
}
free(gpu_lib);

Copilot uses AI. Check for mistakes.
}
}
}

char *gpu_name_get_new(struct gpu_library *gpu_lib)
{

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpu_name_get_new() dereferences gpu_lib without checking for NULL. Since this is a public API, add a NULL guard (and possibly vendor validation) to avoid a potential crash if callers accidentally pass NULL.

Suggested change
{
{
if (gpu_lib == NULL) {
return NULL;
}

Copilot uses AI. Check for mistakes.
if (gpu_lib->vendor == NVIDIA) {
return nvml_gpu_name(gpu_lib->nvidia_lib);
} else if (gpu_lib->vendor == AMD) {
// todo work in future
char *name = strdup("AMD GPU Not Supported");
if (name == NULL) {
return NULL;
}
return name;
}
return NULL;
}
29 changes: 29 additions & 0 deletions dttools/src/gpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#ifndef GPU_H
#define GPU_H

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "gpu_commons.h"

// generic gpu options which can be used everywhere
struct gpu_library {
enum gpu_vendor vendor;
struct nvml_library * nvidia_lib;
struct amd_rocm_hsa_library * amd_lib;
// ... add more if we decide to support more
};
Comment thread
LaxminarayanaV7416 marked this conversation as resolved.

struct gpu_library * gpu_lib_init();
void gpu_lib_close(struct gpu_library * gpu_lib);
char *gpu_name_get_new(struct gpu_library * gpu_lib);

#endif
49 changes: 49 additions & 0 deletions dttools/src/gpu_commons.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#include "gpu_commons.h"
#include "debug.h"
#include <string.h>

// nvidia libray name as macro
const char *NVIDIA_LIBRARY_SEARCH_COMMAND = "ldconfig -p | grep libnvidia-ml.so | awk '{print $NF}'";
// AMD library need to check for now we are only intrested in Nvidia
// the below library is not verified but referenced for future use case
const char *AMD_LIBRARY_SEARCH_COMMAND = "ldconfig -p | grep libhsa-runtime64.so | awk '{print $NF}'";

struct library_search_result find_library_by_name(enum gpu_vendor vendor)
{
char command[256];
char result[512];
struct library_search_result lib_result = {{0}, false};
if (vendor == NVIDIA) {
strcpy(command, NVIDIA_LIBRARY_SEARCH_COMMAND);
} else if (vendor == AMD) {
strcpy(command, AMD_LIBRARY_SEARCH_COMMAND);
} else {
debug(D_ERROR, "we are not supporting any other GPU's apart from Nvidia or AMD");
return lib_result;
}

FILE *fp = popen(command, "r");
if (fp == NULL) {
debug(D_ERROR, "popen failed to run ldconfig command");
return lib_result;
}

if (fgets(result, sizeof(result), fp) != NULL) {
result[strcspn(result, "\n")] = 0;
strncpy(lib_result.path, result, sizeof(lib_result.path) - 1);
lib_result.path[sizeof(lib_result.path) - 1] = '\0';
lib_result.found = true;
debug(D_DEBUG, "GPU Library found at path %s", lib_result.path);
} else {
debug(D_DEBUG, "GPU Library not found in system search paths.");
}

pclose(fp);
return lib_result;
}
32 changes: 32 additions & 0 deletions dttools/src/gpu_commons.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#ifndef GPU_COMMONS_H
#define GPU_COMMONS_H

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

// enum for the GPU vendor
enum gpu_vendor {
NVIDIA = 0,
AMD = 1,
};

// this says whether the searched library exists or not
struct library_search_result {
char path[512];
bool found;
};

// AMD place holder library
struct amd_rocm_hsa_library {};

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct amd_rocm_hsa_library {} is an empty struct; empty structs are not valid in standard C and will fail to compile with many toolchains/flags. Use a forward declaration (struct amd_rocm_hsa_library;) or give it at least one dummy member.

Suggested change
struct amd_rocm_hsa_library {};
struct amd_rocm_hsa_library;

Copilot uses AI. Check for mistakes.

struct library_search_result find_library_by_name(enum gpu_vendor vendor);

#endif
129 changes: 129 additions & 0 deletions dttools/src/nvidia_nvml_library.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
Copyright (C) 2022 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#include "nvidia_nvml_library.h"
#include "debug.h"
#include <string.h>

#define LOAD_SYMBOL(handle, field, type, name) \
do { \
dlerror(); \
(field) = (type)dlsym((handle), (name)); \
const char *sym_err = dlerror(); \
if (sym_err) { \
debug(D_ERROR, "dlsym failed for %s: %s", (name), sym_err); \
goto fail; \
} \
} while (0)

struct nvml_library *nvml_library_open(struct library_search_result res)
{
struct nvml_library *nvml = calloc(1, sizeof(*nvml));
if (!nvml) {
debug(D_ERROR, "Out of memory allocating nvml_library");
return NULL;
}

if (!res.found || res.path[0] == '\0') {
debug(D_ERROR, "Error: GPU Library path was not found.");
free(nvml);
return NULL;
}

debug(D_DEBUG, "GPU library opening from path %s", res.path);
nvml->lib_handle = dlopen(res.path, RTLD_NOW | RTLD_LOCAL);
if (!nvml->lib_handle) {
debug(D_ERROR, "dlopen failed: %s", dlerror());
free(nvml);
return NULL;
}

LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlInit, nvmlReturn_t (*)(void), "nvmlInit");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlShutdown, nvmlReturn_t (*)(void), "nvmlShutdown");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlDeviceGetHandleByIndex, nvmlReturn_t (*)(int, nvmlDevice_t *), "nvmlDeviceGetHandleByIndex");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlDeviceGetMemoryInfo, nvmlReturn_t (*)(nvmlDevice_t, nvmlMemory_t *), "nvmlDeviceGetMemoryInfo");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlDeviceGetUtilizationRates, nvmlReturn_t (*)(nvmlDevice_t, nvmlUtilization_t *), "nvmlDeviceGetUtilizationRates");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlDeviceGetCount, nvmlReturn_t (*)(int *), "nvmlDeviceGetCount");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlDeviceGetName, nvmlReturn_t (*)(nvmlDevice_t, char *, unsigned int), "nvmlDeviceGetName");
LOAD_SYMBOL(nvml->lib_handle, nvml->nvmlErrorString, char *(*)(nvmlReturn_t), "nvmlErrorString");

debug(D_DEBUG, "library nvml load completed!");

nvmlReturn_t init_result = nvml->nvmlInit();
debug(D_DEBUG, "initialization result %d", init_result);
if (init_result != NVML_SUCCESS) {
debug(D_ERROR, "GPU initialization failed with error code %i", init_result);
goto fail;
}
return nvml;

fail:
nvml_library_close(nvml);
return NULL;
}

void nvml_library_close(struct nvml_library *lib)
{
if (!lib) {
return;
}
// now call the shutdown function to properly close the nvml lib
if (lib->nvmlShutdown) {
nvmlReturn_t shutdown_result = lib->nvmlShutdown();
if (shutdown_result != NVML_SUCCESS) {
debug(D_ERROR, "GPU shutdown failed with error code %i", shutdown_result);
// doesnt matter now close the lib anyways
}
}
if (lib->lib_handle) {
dlclose(lib->lib_handle);
}
free(lib);
}

char *nvml_gpu_name(struct nvml_library *nvml_lib)
{
if (!nvml_lib) {
debug(D_ERROR, "nvml_gpu_name called with NULL nvml_lib");
return NULL;
}

int device_count = 0;
nvmlReturn_t count_result = nvml_lib->nvmlDeviceGetCount(&device_count);
if (count_result != NVML_SUCCESS) {
debug(D_ERROR, "Failed to get device count: %s", nvml_lib->nvmlErrorString(count_result));
return NULL;
}

if (device_count <= 0) {
debug(D_ERROR, "No GPU devices found");
return NULL;
}

char *name = malloc(NVML_DEVICE_NAME_BUFFER_SIZE);
if (!name) {
debug(D_ERROR, "Out of memory allocating GPU name buffer");
return NULL;
}

nvmlDevice_t device;
nvmlReturn_t result = nvml_lib->nvmlDeviceGetHandleByIndex(0, &device);
if (result != NVML_SUCCESS) {
debug(D_ERROR, "Failed to get device handle: %s", nvml_lib->nvmlErrorString(result));
free(name);
return NULL;
}

result = nvml_lib->nvmlDeviceGetName(device, name, NVML_DEVICE_NAME_BUFFER_SIZE);
if (result != NVML_SUCCESS) {
debug(D_ERROR, "Failed to get device name: %s", nvml_lib->nvmlErrorString(result));
free(name);
return NULL;
}

debug(D_DEBUG, "GPU Name: %s", name);
return name;
}
Loading
Loading