Skip to content
Merged
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
18 changes: 12 additions & 6 deletions src/platform/linux/cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ namespace cuda {
static cdf_t cdf;

/**
* @brief Log a failed call through a CUDA function table.
* @brief Log a failed CUDA call with its numeric result and safe fallback error text.
*
* Error lookups may fail or return null, so only use text from successful, non-null lookups.
*
* @param functions CUDA function table.
* @param result Result of the call.
Expand All @@ -110,13 +112,17 @@ namespace cuda {
*/
inline static int check(const CudaFunctions &functions, CUresult result, const std::string_view &sv) {
if (result != CUDA_SUCCESS) {
const char *name;
const char *description;
const char *name = nullptr;
const char *description = nullptr;

functions.cuGetErrorName(result, &name);
functions.cuGetErrorString(result, &description);
if (functions.cuGetErrorName(result, &name) != CUDA_SUCCESS || !name) {
name = "Unknown CUDA error";
}
if (functions.cuGetErrorString(result, &description) != CUDA_SUCCESS || !description) {
description = "No error description available";
}

BOOST_LOG(error) << sv << name << ':' << description;
BOOST_LOG(error) << sv << name << " (" << static_cast<int>(result) << "): " << description;
return -1;
}

Expand Down
86 changes: 86 additions & 0 deletions tests/unit/platform/linux/test_cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <fstream>
#include <latch>
#include <map>
#include <memory>
Expand Down Expand Up @@ -2185,6 +2186,91 @@ TEST_F(CudaGlDriverTest, PlaneTextureRegistration) {
EXPECT_EQ(cuda::register_plane_texture(api().cuda, &resource, 8), -1);
}

namespace {
/**
* @brief CUDA error lookup behavior and the text that is safe to log.
*/
struct cuda_error_lookup_case_t {
tcuGetErrorName *lookup; ///< Error name or description lookup with the shared CUDA signature.
const char *expected; ///< Text to log, or nullptr when the appropriate fallback is required.
};
} // namespace

TEST(CudaErrorLoggingTest, HandlesLookupFailuresAndNullTextIndependently) {
const std::array cases {
cuda_error_lookup_case_t {[](CUresult, const char **text) {
*text = "resolved";
return CUDA_SUCCESS;
},
"resolved"},
cuda_error_lookup_case_t {[](CUresult, const char **) {
return cu_failed;
},
nullptr},
cuda_error_lookup_case_t {[](CUresult, const char **text) {
*text = reinterpret_cast<const char *>(std::uintptr_t {1});
return cu_failed;
},
nullptr},
cuda_error_lookup_case_t {[](CUresult, const char **text) {
*text = nullptr;
return CUDA_SUCCESS;
},
nullptr},
};

for (std::size_t name_case = 0; name_case < cases.size(); ++name_case) {
for (std::size_t description_case = 0; description_case < cases.size(); ++description_case) {
SCOPED_TRACE(testing::Message() << "name=" << name_case << ", description=" << description_case);
api() = fake_api_t {};
auto functions = fake_cuda_functions();
functions.cuGetErrorName = cases[name_case].lookup;
functions.cuGetErrorString = cases[description_case].lookup;
functions.cuGraphicsGLRegisterImage = [](CUgraphicsResource *, GLuint, GLenum, unsigned int) {
return static_cast<CUresult>(100); // CUDA_ERROR_NO_DEVICE is not named in nv-codec-headers.
};
CUgraphicsResource resource = nullptr;
logging::log_flush();
std::ifstream log_file {"test_sunshine.log", std::ios::ate};
ASSERT_TRUE(log_file);
const auto log_position = log_file.tellg();

EXPECT_EQ(cuda::register_plane_texture(functions, &resource, 7), -1);

logging::log_flush();
log_file.seekg(log_position);
std::string line;
ASSERT_TRUE(static_cast<bool>(std::getline(log_file, line)));
const std::string expected = std::string {"Couldn't register plane texture: "} +
(cases[name_case].expected ? cases[name_case].expected : "Unknown CUDA error") +
" (100): " +
(cases[description_case].expected ? cases[description_case].expected : "No error description available");
EXPECT_NE(line.find(expected), std::string::npos) << line;
}
}
}

TEST(CudaErrorLoggingTest, SuccessfulCallsDoNotLookUpOrLogErrors) {
api() = fake_api_t {};
auto functions = fake_cuda_functions();
functions.cuGetErrorName = functions.cuGetErrorString = [](CUresult, const char **) {
ADD_FAILURE() << "Successful CUDA calls must not look up errors";
return cu_failed;
};
CUgraphicsResource resource = nullptr;
logging::log_flush();
std::ifstream log_file {"test_sunshine.log", std::ios::ate};
ASSERT_TRUE(log_file);
const auto log_position = log_file.tellg();

EXPECT_EQ(cuda::register_plane_texture(functions, &resource, 7), 0);

logging::log_flush();
log_file.seekg(log_position);
EXPECT_EQ(log_file.peek(), std::ifstream::traits_type::eof());
EXPECT_EQ(resource, &api().resources[0]);
}

TEST_F(CudaGlDriverTest, GlCommandsWaitDeletesItsFence) {
EXPECT_EQ(cuda::wait_for_gl_commands(), 0);

Expand Down
Loading