error.hpp
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #pragma once
6 
7 #include <cstring>
8 #include <sstream>
9 #include <stdexcept>
10 #include <string>
11 #include <system_error>
12 
13 #include <kvikio/shim/cuda.hpp>
14 #include <kvikio/shim/cufile_h_wrapper.hpp>
15 
16 namespace kvikio {
17 
18 struct CUfileException : public std::runtime_error {
19  using std::runtime_error::runtime_error;
20 };
21 
22 class GenericSystemError : public std::system_error {
23  public:
24  GenericSystemError(int err_code, std::string const& msg);
25  GenericSystemError(int err_code, char const* msg);
26  GenericSystemError(std::string const& msg);
27  GenericSystemError(char const* msg);
28  GenericSystemError(GenericSystemError const& other) = default;
29  GenericSystemError& operator=(GenericSystemError const& other) = default;
30  virtual ~GenericSystemError() noexcept = default;
31 };
32 
33 #ifndef CUDA_DRIVER_TRY
59 #define CUDA_DRIVER_TRY(...) \
60  GET_CUDA_DRIVER_TRY_MACRO(__VA_ARGS__, CUDA_DRIVER_TRY_2, CUDA_DRIVER_TRY_1) \
61  (__VA_ARGS__)
64 #define GET_CUDA_DRIVER_TRY_MACRO(_1, _2, NAME, ...) NAME
65 #define CUDA_DRIVER_TRY_2(_call, _exception_type) \
66  do { \
67  kvikio::detail::cuda_driver_try_2<_exception_type>(_call, __LINE__, __FILE__); \
68  } while (0)
69 #define CUDA_DRIVER_TRY_1(_call) CUDA_DRIVER_TRY_2(_call, kvikio::CUfileException)
70 #endif
71 
72 #ifndef CUFILE_TRY
98 #define CUFILE_TRY(...) \
99  GET_CUFILE_TRY_MACRO(__VA_ARGS__, CUFILE_TRY_2, CUFILE_TRY_1) \
100  (__VA_ARGS__)
103 #define GET_CUFILE_TRY_MACRO(_1, _2, NAME, ...) NAME
104 #define CUFILE_TRY_2(_call, _exception_type) \
105  do { \
106  kvikio::detail::cufile_try_2<_exception_type>(_call, __LINE__, __FILE__); \
107  } while (0)
108 #define CUFILE_TRY_1(_call) CUFILE_TRY_2(_call, kvikio::CUfileException)
109 #endif
110 
111 #ifndef CUFILE_CHECK_BYTES_DONE
112 #define CUFILE_CHECK_BYTES_DONE(...) \
113  GET_CUFILE_CHECK_BYTES_DONE_MACRO( \
114  __VA_ARGS__, CUFILE_CHECK_BYTES_DONE_2, CUFILE_CHECK_BYTES_DONE_1) \
115  (__VA_ARGS__)
116 #define GET_CUFILE_CHECK_BYTES_DONE_MACRO(_1, _2, NAME, ...) NAME
117 #define CUFILE_CHECK_BYTES_DONE_2(_nbytes_done, _exception_type) \
118  do { \
119  kvikio::detail::cufile_check_bytes_done_2<_exception_type>(_nbytes_done, __LINE__, __FILE__); \
120  } while (0)
121 #define CUFILE_CHECK_BYTES_DONE_1(_call) CUFILE_CHECK_BYTES_DONE_2(_call, kvikio::CUfileException)
122 #endif
123 
124 namespace detail {
125 template <typename Exception>
126 void cuda_driver_try_2(CUresult error, int line_number, char const* filename)
127 {
128  if (error == CUDA_ERROR_STUB_LIBRARY) {
129  throw Exception{std::string{"CUDA error at: "} + std::string(filename) + ":" +
130  std::to_string(line_number) +
131  ": CUDA_ERROR_STUB_LIBRARY("
132  "The CUDA driver loaded is a stub library)"};
133  }
134  if (error != CUDA_SUCCESS) {
135  char const* err_name = nullptr;
136  char const* err_str = nullptr;
137  CUresult err_name_status = cudaAPI::instance().GetErrorName(error, &err_name);
138  CUresult err_str_status = cudaAPI::instance().GetErrorString(error, &err_str);
139  if (err_name_status == CUDA_ERROR_INVALID_VALUE) { err_name = "unknown"; }
140  if (err_str_status == CUDA_ERROR_INVALID_VALUE) { err_str = "unknown"; }
141  throw Exception{std::string{"CUDA error at: "} + filename + ":" + std::to_string(line_number) +
142  ": " + std::string(err_name) + "(" + std::string(err_str) + ")"};
143  }
144 }
145 
146 template <typename Exception>
147 void cufile_try_2(CUfileError_t error, int line_number, char const* filename)
148 {
149  if (error.err != CU_FILE_SUCCESS) {
150  if (error.err == CU_FILE_CUDA_DRIVER_ERROR) {
151  CUresult const cuda_error = error.cu_err;
152  CUDA_DRIVER_TRY(cuda_error);
153  }
154  throw Exception{std::string{"cuFile error at: "} + filename + ":" +
155  std::to_string(line_number) + ": " +
156  cufileop_status_error((CUfileOpError)std::abs(error.err))};
157  }
158 }
159 
160 template <typename Exception>
161 void cufile_check_bytes_done_2(ssize_t nbytes_done, int line_number, char const* filename)
162 {
163  if (nbytes_done < 0) {
164  auto const err = std::abs(nbytes_done);
165  auto const msg = (err > CUFILEOP_BASE_ERR)
166  ? std::string(cufileop_status_error((CUfileOpError)err))
167  : std::string(std::strerror(err));
168  throw Exception{std::string{"cuFile error at: "} + filename + ":" +
169  std::to_string(line_number) + ": " + msg};
170  }
171 }
172 
173 #define KVIKIO_LOG_ERROR(err_msg) kvikio::detail::log_error(err_msg, __LINE__, __FILE__)
174 void log_error(std::string_view err_msg, int line_number, char const* filename);
175 
176 } // namespace detail
177 
207 #define KVIKIO_EXPECT(...) \
208  GET_KVIKIO_EXPECT_MACRO(__VA_ARGS__, KVIKIO_EXPECT_3, KVIKIO_EXPECT_2)(__VA_ARGS__)
211 #define GET_KVIKIO_EXPECT_MACRO(_1, _2, _3, NAME, ...) NAME
212 
213 #define KVIKIO_EXPECT_3(_condition, _msg, _exception_type) \
214  do { \
215  kvikio::detail::kvikio_assertion<_exception_type>(_condition, _msg, __LINE__, __FILE__); \
216  } while (0)
217 
218 #define KVIKIO_EXPECT_2(_condition, _msg) KVIKIO_EXPECT_3(_condition, _msg, kvikio::CUfileException)
219 
243 #define KVIKIO_FAIL(...) \
244  GET_KVIKIO_FAIL_MACRO(__VA_ARGS__, KVIKIO_FAIL_2, KVIKIO_FAIL_1)(__VA_ARGS__)
247 #define GET_KVIKIO_FAIL_MACRO(_1, _2, NAME, ...) NAME
248 
249 #define KVIKIO_FAIL_2(_msg, _exception_type) \
250  do { \
251  kvikio::detail::kvikio_assertion<_exception_type>(false, _msg, __LINE__, __FILE__); \
252  } while (0)
253 
254 #define KVIKIO_FAIL_1(_msg) KVIKIO_FAIL_2(_msg, kvikio::CUfileException)
255 
256 namespace detail {
257 template <typename Exception>
258 void kvikio_assertion(bool condition, const char* msg, int line_number, char const* filename)
259 {
260  if (!condition) {
261  std::stringstream ss;
262  ss << "KvikIO failure at: " << filename << ":" << line_number << ": ";
263  if (msg == nullptr) {
264  ss << "(no message)";
265  } else {
266  ss << msg;
267  }
268  throw Exception{ss.str()};
269  };
270 }
271 
272 template <typename Exception>
273 void kvikio_assertion(bool condition, const std::string& msg, int line_number, char const* filename)
274 {
275  kvikio_assertion<Exception>(condition, msg.c_str(), line_number, filename);
276 }
277 } // namespace detail
278 
317 #define SYSCALL_CHECK(...) \
318  GET_SYSCALL_CHECK_MACRO(__VA_ARGS__, SYSCALL_CHECK_3, SYSCALL_CHECK_2, SYSCALL_CHECK_1) \
319  (__VA_ARGS__)
322 #define GET_SYSCALL_CHECK_MACRO(_1, _2, _3, NAME, ...) NAME
323 #define SYSCALL_CHECK_1(_return_value) \
324  do { \
325  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value); \
326  } while (0)
327 #define SYSCALL_CHECK_2(_return_value, _extra_msg) \
328  do { \
329  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value, _extra_msg); \
330  } while (0)
331 #define SYSCALL_CHECK_3(_return_value, _extra_msg, _error_value) \
332  do { \
333  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value, _extra_msg, _error_value); \
334  } while (0)
335 
336 namespace detail {
337 void handle_linux_call_error(int line_number, char const* filename, std::string_view extra_msg);
338 
339 inline void check_linux_call(int line_number,
340  char const* filename,
341  int return_value,
342  std::string_view extra_msg = "",
343  int error_value = -1)
344 {
345  if (return_value == error_value) { handle_linux_call_error(line_number, filename, extra_msg); }
346 }
347 
348 template <typename T>
349 void check_linux_call(
350  int line_number, char const* filename, T return_value, std::string_view extra_msg, T error_value)
351 {
352  if (return_value == error_value) { handle_linux_call_error(line_number, filename, extra_msg); }
353 }
354 } // namespace detail
355 
356 } // namespace kvikio
#define CUDA_DRIVER_TRY(...)
Error checking macro for CUDA driver API functions.
Definition: error.hpp:59
KvikIO namespace.
Definition: batch.hpp:16