Improve tooling for error handling
- Dominant language
- C++
- Stars
- 2.5k
- Forks
- 487
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 296
Description
CUDA API function calls may fail in a variety of ways, some of which are more frequent than in host code (e.g. memory allocation). Therefore, coders need all the help they can get, which is the simple motivation behind this issue. The approach is multi-pronged:
1. Implement `std::`(`experimental::`) `expected`, `scope_exit`, `scope_fail`, and `scope_success`
These are time-tested tools that help with restoring the correct system state in the presence of errors. `expected` allows APIs that can be used with or without exceptions, and `scope_*` allow simple, correct transactional code in the presence of any early returns.
2. Standardize the `CUDA_SAFE_CALL` (or similar) function/macro that everybody and their cat are defining on their own. Writing such n artifact has become a rite of passage of any programmer going from "I'm trying CUDA" to "I'm writing a CUDA application". Factoring such code into libcu++ will have a terrific reuse rate.
I gratuitously include the ~~macro~~function we defined in our own app, partly to illustrate how inconsistent error information fetching is across CUDA/host, CUDA/device, cuBLAS, and cuSolver.
```C++
template
void cuda_safe_call(
const T status, const std::experimental::source_location loc = std::experimental::source_location::current()) {
// All "success" statuses are zero
static_assert(cudaSuccess == 0 && CUDA_SUCCESS == 0 && CUBLAS_STATUS_SUCCESS == 0 && CUSOLVER_STATUS_SUCCESS == 0,
"Please revise this function.");
// Common early exit test for all cases
if (status == 0)
return;
int dev = -1;
cudaGetDevice(&dev);
if constexpr (std::is_same_v) {
fprintf(stderr, "%s(%u:%u) [device %d] CUDA error in %s: %s (%s).\n", loc.file_name(), loc.line(), loc.column(),
dev, loc.function_name(), cudaGetErrorString(status), cudaGetErrorName(status));
} else if constexpr (std::is_same_v) {
const char* error_string;
cuGetErrorString(status, &error_string);
const char* error_name;
cuGetErrorName(status, &error_name);
fprintf(stderr, "%s(%u:%u) [device %d] CUDA DRIVER error in %s: %s (%s).\n", loc.file_name(), loc.line(),
loc.column(), dev, loc.function_name(), error_string, error_name);
} else if constexpr (std::is_same_v) {
fprintf(stderr, "%s(%u:%u) [device %d] CUBLAS error in %s: %s.\n", loc.file_name(), loc.line(), loc.column(),
dev, loc.function_name(), cublasGetStatusString(status));
} else {
static_assert(std::is_same_v, "Error: not a CUDA status.");
fprintf(stderr, "%s(%u:%u) [device %d] CUSOLVER error in call %s: %s.\n", loc.file_name(), loc.line(),
loc.column(), dev, loc.function_name(), cusolverGetErrorString(status));
}
abort();
}
```
Contributor guide
Research direction
No repository files or tests are named. Start by surveying the libcu++ error-handling and CUDA status APIs, then determine how expected, scope guards, and a shared CUDA error utility should fit together. Done means the proposed tools and standardized error handling cover the stated host, device, cuBLAS, and cuSolver cases with project tests or examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend-api-design, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100