Skip to content
Merged
11 changes: 11 additions & 0 deletions sdk_v2/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ find_package(nlohmann_json CONFIG REQUIRED)
find_package(azure-storage-blobs-cpp CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(Microsoft.GSL CONFIG REQUIRED)
find_package(LibArchive REQUIRED)

if(FOUNDRY_LOCAL_BUILD_SERVICE)
find_package(oatpp CONFIG REQUIRED)
endif()

if(FOUNDRY_LOCAL_BUILD_TESTS)
find_package(GTest CONFIG REQUIRED)
find_package(ZLIB REQUIRED)
enable_testing()
endif()

Expand Down Expand Up @@ -161,8 +163,11 @@ set(FOUNDRY_LOCAL_SOURCES
src/download/inference_model_writer.cc
src/download/model_registry_client.cc
src/ep_detection/cuda_ep_bootstrapper.cc
src/ep_detection/cuda_ep_manifest.cc
src/ep_detection/ep_bundle_installer.cc
src/ep_detection/ep_detector.cc
src/ep_detection/ep_utils.cc
src/ep_detection/nvml_gpu_detector.cc
src/ep_detection/runtime_version_info.cc
src/ep_detection/webgpu_ep_bootstrapper.cc
src/exception.cc
Expand Down Expand Up @@ -239,8 +244,14 @@ function(foundry_local_configure_target TARGET LINK_SCOPE)
Azure::azure-core
Azure::azure-storage-blobs
spdlog::spdlog
LibArchive::LibArchive
${CMAKE_DL_LIBS}
)

if(WIN32)
target_link_libraries(${TARGET} ${LINK_SCOPE} shell32 ole32)
endif()

if(TARGET OnnxRuntimeGenAI::OnnxRuntimeGenAI)
target_link_libraries(${TARGET} ${LINK_SCOPE}
OnnxRuntimeGenAI::OnnxRuntimeGenAI
Expand Down
252 changes: 97 additions & 155 deletions sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,84 @@
// Licensed under the MIT License.
#include "ep_detection/cuda_ep_bootstrapper.h"

#include "ep_detection/cuda_ep_manifest.h"
#include "ep_detection/ep_utils.h"
#include "ep_detection/nvml_gpu_detector.h"
#include "logger.h"
#include "util/file_lock.h"
#include "utils.h"
#include "http/http_download.h"
#include "util/zip_extract.h"

#include <fmt/format.h>

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstdio>
#include <filesystem>
#include <string>

#if defined(__linux__) && !defined(__ANDROID__)
#include <dlfcn.h>
#endif

namespace {

constexpr const char* kPackageFileName = "cuda-ep.zip";
constexpr const char* kLockFileName = "cuda-ep.lock";
constexpr const char* kUserAgent = "FoundryLocal";
constexpr int kMaxInstallAttempts = 5;
constexpr const char* kRegistrationName = "CUDAExecutionProvider";
constexpr const char* kCudaProviderOverrideEnv = "FOUNDRY_LOCAL_CUDA_EP_LIBRARY";
#if defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__)
constexpr const char* kGenAiCudaLibrary = "libonnxruntime-genai-cuda.so";
#endif

// CUDA EP package is built against the ONNX Runtime version we link against.
constexpr const char* kDownloadUrl =
"https://foundrypackages-ffhrdhbxb7gpdreh.b02.azurefd.net/cuda-ep-20260501-062935.zip";
fl::CudaEpPlatform HostCudaEpPlatform() {
#if defined(_WIN32) && defined(_M_ARM64)
return fl::CudaEpPlatform::WindowsArm64;
#elif defined(_WIN32) && defined(_M_X64)
return fl::CudaEpPlatform::WindowsX64;
#elif defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__)
return fl::CudaEpPlatform::LinuxX64;
#elif defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__)
return fl::CudaEpPlatform::LinuxArm64;
#else
return fl::CudaEpPlatform::Unsupported;
#endif
}

struct ExpectedBinary {
const char* filename;
const char* sha256;
};
#if defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__)
bool LoadGenAiCudaLibrary(const std::filesystem::path& path, void*& handle, fl::ILogger& logger) {
if (handle) {
return true;
}

constexpr ExpectedBinary kExpectedBinaries[] = {
{"onnxruntime_providers_cuda.dll", "DD540FCFECFBC68B4675C9ADF09C2858CF6B054563859D79598AA2524406A76F"},
{"onnxruntime-genai-cuda.dll", "BC953F8E2AAFC6219B2D723B65AB8F1A9426A6B7724D6A01ED756FAE8C3DE6AE"},
};
dlerror();
handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
if (!handle) {
const char* error = dlerror();
logger.Log(fl::LogLevel::Warning,
fmt::format("CUDA EP: failed to load '{}' ({})", path.string(), error ? error : "unknown error"));
return false;
}

constexpr const char* kRegistrationName = "Foundry.CUDA";
constexpr const char* kCudaProviderDll = "onnxruntime_providers_cuda.dll";
constexpr const char* kCudaProviderOverrideEnv = "FOUNDRY_LOCAL_CUDA_EP_LIBRARY";
return true;
}
#endif

} // anonymous namespace

namespace fl {

CudaEpBootstrapper::CudaEpBootstrapper(std::string ep_dir, EpRegistrationCallback register_ep)
: ep_dir_(std::move(ep_dir)), register_ep_(std::move(register_ep)) {}
CudaEpBootstrapper::CudaEpBootstrapper(std::string root_dir, EpRegistrationCallback register_ep)
: register_ep_(std::move(register_ep)), installer_(std::filesystem::path(root_dir), kLockFileName, "CUDA EP") {}

const std::string& CudaEpBootstrapper::Name() const {
return name_;
CudaEpBootstrapper::~CudaEpBootstrapper() {
#if defined(__linux__) && !defined(__ANDROID__)
if (genai_cuda_handle_) {
dlclose(genai_cuda_handle_);
}
#endif
}

bool CudaEpBootstrapper::IsRegistered() const {
return registered_;
}
const std::string& CudaEpBootstrapper::Name() const { return name_; }

bool CudaEpBootstrapper::DownloadAndRegister(bool force,
const ProgressCallback& progress_cb,
ILogger& logger) {
bool CudaEpBootstrapper::IsRegistered() const { return registered_; }

bool CudaEpBootstrapper::DownloadAndRegister(bool force, const ProgressCallback& progress_cb, ILogger& logger) {
if (registered_ && !force) {
if (progress_cb) {
progress_cb(name_, 100.0f);
Expand All @@ -75,34 +94,34 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force,

attempts_++;

auto ep_dir = std::filesystem::path(ep_dir_);
auto lock_path = ep_dir.parent_path() / kLockFileName;
auto zip_path = ep_dir.parent_path() / kPackageFileName;

try {
auto override_path = Utils::GetEnv(kCudaProviderOverrideEnv);
if (override_path.has_value() && !override_path->empty()) {
std::filesystem::path provider_path(*override_path);
std::filesystem::path provider_path = std::filesystem::absolute(*override_path);

if (!std::filesystem::exists(provider_path)) {
logger.Log(LogLevel::Warning,
fmt::format("CUDA EP: {} set but file does not exist ({})",
kCudaProviderOverrideEnv, provider_path.string()));
logger.Log(LogLevel::Warning, fmt::format("CUDA EP: {} set but file does not exist ({})",
kCudaProviderOverrideEnv, provider_path.string()));
return false;
}

if (progress_cb) {
progress_cb(name_, 90.0f);
if (progress_cb && !progress_cb(name_, 90.0f)) {
return false;
}

// Prepend the override directory to PATH so sibling dependency DLLs are discoverable,
// matching the normal install path. The provider DLL delay-loads CUDA/cuDNN dependencies.
#ifdef _WIN32
PrependDirToProcessPath(provider_path.parent_path());
#endif

#if defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__)
if (!LoadGenAiCudaLibrary(provider_path.parent_path() / kGenAiCudaLibrary, genai_cuda_handle_, logger)) {
return false;
}
#endif

if (!register_ep_(kRegistrationName, provider_path)) {
logger.Log(LogLevel::Warning,
fmt::format("CUDA EP: ORT registration failed for override {}={}",
kCudaProviderOverrideEnv, provider_path.string()));
logger.Log(LogLevel::Warning, fmt::format("CUDA EP: ORT registration failed for override {}={}",
kCudaProviderOverrideEnv, provider_path.string()));
return false;
}

Expand All @@ -112,89 +131,40 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force,
progress_cb(name_, 100.0f);
}

logger.Log(LogLevel::Information,
fmt::format("CUDA EP: ready (override_env={} install_path={})",
kCudaProviderOverrideEnv, provider_path.string()));
logger.Log(LogLevel::Information, fmt::format("CUDA EP: ready (override_env={} install_path={})",
kCudaProviderOverrideEnv, provider_path.string()));
return true;
}

// Cross-process lock to prevent concurrent installs
FileLock lock(lock_path);

// Check if package already exists and is valid
if (fl::VerifyEpBinaries(ep_dir,
{{kExpectedBinaries[0].filename, kExpectedBinaries[0].sha256},
{kExpectedBinaries[1].filename, kExpectedBinaries[1].sha256}},
"CUDA EP", logger)) {
logger.Log(LogLevel::Information, "CUDA EP: package already valid, skipping download");
} else {
// Clean up any partial install
if (std::filesystem::exists(ep_dir)) {
std::filesystem::remove_all(ep_dir);
}

std::filesystem::create_directories(ep_dir);

// Download
logger.Log(LogLevel::Information, "CUDA EP: downloading from CDN...");

// Bridge callback-based cancellation to the atomic flag HttpDownloadFile expects
std::atomic<bool> cancel_flag{false};

auto download_progress = [&](float pct) {
if (progress_cb) {
// 0-80% for download phase
if (!progress_cb(name_, pct * 0.8f)) {
cancel_flag.store(true);
}
}
};

if (!HttpDownloadFile(kDownloadUrl, zip_path, kUserAgent,
&cancel_flag, download_progress, logger)) {
logger.Log(LogLevel::Warning, "CUDA EP: download failed (see prior log for details)");
return false;
}

// Extract
logger.Log(LogLevel::Information, "CUDA EP: extracting...");

if (!ExtractZip(zip_path, ep_dir, logger)) {
logger.Log(LogLevel::Warning, "CUDA EP: extraction failed");
return false;
}

// Clean up zip
std::filesystem::remove(zip_path);
auto manifest = BuildCudaEpManifest(HostCudaEpPlatform());
if (!manifest.has_value()) {
logger.Log(LogLevel::Warning, "CUDA EP: no bundle available for this platform");
return false;
}

// Verify
if (!fl::VerifyEpBinaries(ep_dir,
{{kExpectedBinaries[0].filename, kExpectedBinaries[0].sha256},
{kExpectedBinaries[1].filename, kExpectedBinaries[1].sha256}},
"CUDA EP", logger)) {
logger.Log(LogLevel::Warning, "CUDA EP: verification failed after download");
return false;
}
const auto install_policy = force ? EpBundleInstallPolicy::ForceDownload : EpBundleInstallPolicy::ReuseVerified;
auto txn = installer_.EnsureInstalled(*manifest, progress_cb, logger, install_policy);
if (!txn) {
return false;
}

if (progress_cb) {
progress_cb(name_, 90.0f);
if (!txn->CommitActive(logger)) {
logger.Log(LogLevel::Warning, "CUDA EP: failed to publish active bundle marker");
return false;
}

// Register with ORT
const auto provider_path = txn->bin_dir() / manifest->provider_relative_path;
#ifdef _WIN32
// Permanently prepend the EP directory to PATH. The zip bundles all
// required CUDA/cuDNN DLLs, so no system CUDA install is needed.
// PATH must stay modified for the process lifetime because:
// - onnxruntime_providers_cuda.dll delay-loads some dependencies
// - onnxruntime-genai-cuda.dll is loaded later at model-load time
// - ORT creates CUDA sessions after registration
PrependDirToProcessPath(ep_dir);
Comment thread
skottmckay marked this conversation as resolved.
if (!dependency_owner_.Load(txn->bin_dir(), *manifest, "CUDA EP", logger)) {
return false;
}
#elif defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__)
if (!LoadGenAiCudaLibrary(txn->bin_dir() / kGenAiCudaLibrary, genai_cuda_handle_, logger)) {
return false;
}
#endif

auto cuda_dll_path = ep_dir / kCudaProviderDll;

if (!register_ep_(kRegistrationName, cuda_dll_path)) {
if (!register_ep_(kRegistrationName, provider_path)) {
logger.Log(LogLevel::Warning, "CUDA EP: ORT registration failed");
return false;
}
Expand All @@ -205,51 +175,23 @@ bool CudaEpBootstrapper::DownloadAndRegister(bool force,
progress_cb(name_, 100.0f);
}

// Bootstrapper-side log — captures the install dir, which the central
// register_ep callback (logs library + version) doesn't have.
logger.Log(LogLevel::Information,
fmt::format("CUDA EP: ready (install_path={})", ep_dir.string()));
logger.Log(LogLevel::Information, fmt::format("CUDA EP: ready (install_path={})", txn->bin_dir().string()));
return true;
} catch (const std::exception& e) {
logger.Log(LogLevel::Warning, fmt::format("CUDA EP: error: {}", e.what()));
return false;
}
}

bool CudaEpBootstrapper::HasNvidiaGpu() {
#ifdef _WIN32
FILE* pipe = _popen("nvidia-smi --query-gpu=compute_cap --format=csv,noheader,nounits 2>nul", "r");
#else
FILE* pipe = popen("nvidia-smi --query-gpu=compute_cap --format=csv,noheader,nounits 2>/dev/null", "r");
#endif

if (!pipe) {
return false;
}

char buffer[128];
std::string result;
while (fgets(buffer, sizeof(buffer), pipe)) {
result += buffer;
}
bool CudaEpBootstrapper::HasNvidiaGpu() { return NvmlGpuDetector::HasNvidiaGpu(); }

#ifdef _WIN32
int exit_code = _pclose(pipe);
bool CudaEpBootstrapper::IsSupportedPlatform() {
#if (defined(_WIN32) && (defined(_M_ARM64) || defined(_M_X64))) || \
(defined(__linux__) && defined(__x86_64__) && !defined(__ANDROID__))
return true;
#else
int exit_code = pclose(pipe);
return false;
#endif

if (exit_code != 0 || result.empty()) {
return false;
}

// Need compute capability >= 5.0 for CUDA 12
try {
float compute_cap = std::stof(result);
return compute_cap >= 5.0f;
} catch (...) {
return false;
}
}

} // namespace fl
Loading
Loading