error.hpp
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2021-2025, 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(const std::string& msg);
25  GenericSystemError(const char* msg);
26  GenericSystemError(const GenericSystemError& other) = default;
27  GenericSystemError& operator=(const GenericSystemError& other) = default;
28  virtual ~GenericSystemError() noexcept = default;
29 };
30 
31 #ifndef CUDA_DRIVER_TRY
57 #define CUDA_DRIVER_TRY(...) \
58  GET_CUDA_DRIVER_TRY_MACRO(__VA_ARGS__, CUDA_DRIVER_TRY_2, CUDA_DRIVER_TRY_1) \
59  (__VA_ARGS__)
62 #define GET_CUDA_DRIVER_TRY_MACRO(_1, _2, NAME, ...) NAME
63 #define CUDA_DRIVER_TRY_2(_call, _exception_type) \
64  do { \
65  kvikio::detail::cuda_driver_try_2<_exception_type>(_call, __LINE__, __FILE__); \
66  } while (0)
67 #define CUDA_DRIVER_TRY_1(_call) CUDA_DRIVER_TRY_2(_call, kvikio::CUfileException)
68 #endif
69 
70 #ifndef CUFILE_TRY
96 #define CUFILE_TRY(...) \
97  GET_CUFILE_TRY_MACRO(__VA_ARGS__, CUFILE_TRY_2, CUFILE_TRY_1) \
98  (__VA_ARGS__)
101 #define GET_CUFILE_TRY_MACRO(_1, _2, NAME, ...) NAME
102 #define CUFILE_TRY_2(_call, _exception_type) \
103  do { \
104  kvikio::detail::cufile_try_2<_exception_type>(_call, __LINE__, __FILE__); \
105  } while (0)
106 #define CUFILE_TRY_1(_call) CUFILE_TRY_2(_call, kvikio::CUfileException)
107 #endif
108 
109 #ifndef CUFILE_CHECK_BYTES_DONE
110 #define CUFILE_CHECK_BYTES_DONE(...) \
111  GET_CUFILE_CHECK_BYTES_DONE_MACRO( \
112  __VA_ARGS__, CUFILE_CHECK_BYTES_DONE_2, CUFILE_CHECK_BYTES_DONE_1) \
113  (__VA_ARGS__)
114 #define GET_CUFILE_CHECK_BYTES_DONE_MACRO(_1, _2, NAME, ...) NAME
115 #define CUFILE_CHECK_BYTES_DONE_2(_nbytes_done, _exception_type) \
116  do { \
117  kvikio::detail::cufile_check_bytes_done_2<_exception_type>(_nbytes_done, __LINE__, __FILE__); \
118  } while (0)
119 #define CUFILE_CHECK_BYTES_DONE_1(_call) CUFILE_CHECK_BYTES_DONE_2(_call, kvikio::CUfileException)
120 #endif
121 
122 namespace detail {
123 template <typename Exception>
124 void cuda_driver_try_2(CUresult error, int line_number, char const* filename)
125 {
126  if (error == CUDA_ERROR_STUB_LIBRARY) {
127  throw Exception{std::string{"CUDA error at: "} + std::string(filename) + ":" +
128  std::to_string(line_number) +
129  ": CUDA_ERROR_STUB_LIBRARY("
130  "The CUDA driver loaded is a stub library)"};
131  }
132  if (error != CUDA_SUCCESS) {
133  char const* err_name = nullptr;
134  char const* err_str = nullptr;
135  CUresult err_name_status = cudaAPI::instance().GetErrorName(error, &err_name);
136  CUresult err_str_status = cudaAPI::instance().GetErrorString(error, &err_str);
137  if (err_name_status == CUDA_ERROR_INVALID_VALUE) { err_name = "unknown"; }
138  if (err_str_status == CUDA_ERROR_INVALID_VALUE) { err_str = "unknown"; }
139  throw Exception{std::string{"CUDA error at: "} + filename + ":" + std::to_string(line_number) +
140  ": " + std::string(err_name) + "(" + std::string(err_str) + ")"};
141  }
142 }
143 
144 template <typename Exception>
145 void cufile_try_2(CUfileError_t error, int line_number, char const* filename)
146 {
147  if (error.err != CU_FILE_SUCCESS) {
148  if (error.err == CU_FILE_CUDA_DRIVER_ERROR) {
149  CUresult const cuda_error = error.cu_err;
150  CUDA_DRIVER_TRY(cuda_error);
151  }
152  throw Exception{std::string{"cuFile error at: "} + filename + ":" +
153  std::to_string(line_number) + ": " +
154  cufileop_status_error((CUfileOpError)std::abs(error.err))};
155  }
156 }
157 
158 template <typename Exception>
159 void cufile_check_bytes_done_2(ssize_t nbytes_done, int line_number, char const* filename)
160 {
161  if (nbytes_done < 0) {
162  auto const err = std::abs(nbytes_done);
163  auto const msg = (err > CUFILEOP_BASE_ERR)
164  ? std::string(cufileop_status_error((CUfileOpError)err))
165  : std::string(std::strerror(err));
166  throw Exception{std::string{"cuFile error at: "} + filename + ":" +
167  std::to_string(line_number) + ": " + msg};
168  }
169 }
170 
171 #define KVIKIO_LOG_ERROR(err_msg) kvikio::detail::log_error(err_msg, __LINE__, __FILE__)
172 void log_error(std::string_view err_msg, int line_number, char const* filename);
173 
174 } // namespace detail
175 
205 #define KVIKIO_EXPECT(...) \
206  GET_KVIKIO_EXPECT_MACRO(__VA_ARGS__, KVIKIO_EXPECT_3, KVIKIO_EXPECT_2)(__VA_ARGS__)
209 #define GET_KVIKIO_EXPECT_MACRO(_1, _2, _3, NAME, ...) NAME
210 
211 #define KVIKIO_EXPECT_3(_condition, _msg, _exception_type) \
212  do { \
213  kvikio::detail::kvikio_assertion<_exception_type>(_condition, _msg, __LINE__, __FILE__); \
214  } while (0)
215 
216 #define KVIKIO_EXPECT_2(_condition, _msg) KVIKIO_EXPECT_3(_condition, _msg, kvikio::CUfileException)
217 
241 #define KVIKIO_FAIL(...) \
242  GET_KVIKIO_FAIL_MACRO(__VA_ARGS__, KVIKIO_FAIL_2, KVIKIO_FAIL_1)(__VA_ARGS__)
245 #define GET_KVIKIO_FAIL_MACRO(_1, _2, NAME, ...) NAME
246 
247 #define KVIKIO_FAIL_2(_msg, _exception_type) \
248  do { \
249  kvikio::detail::kvikio_assertion<_exception_type>(false, _msg, __LINE__, __FILE__); \
250  } while (0)
251 
252 #define KVIKIO_FAIL_1(_msg) KVIKIO_FAIL_2(_msg, kvikio::CUfileException)
253 
254 namespace detail {
255 template <typename Exception>
256 void kvikio_assertion(bool condition, const char* msg, int line_number, char const* filename)
257 {
258  if (!condition) {
259  std::stringstream ss;
260  ss << "KvikIO failure at: " << filename << ":" << line_number << ": ";
261  if (msg == nullptr) {
262  ss << "(no message)";
263  } else {
264  ss << msg;
265  }
266  throw Exception{ss.str()};
267  };
268 }
269 
270 template <typename Exception>
271 void kvikio_assertion(bool condition, const std::string& msg, int line_number, char const* filename)
272 {
273  kvikio_assertion<Exception>(condition, msg.c_str(), line_number, filename);
274 }
275 } // namespace detail
276 
315 #define SYSCALL_CHECK(...) \
316  GET_SYSCALL_CHECK_MACRO(__VA_ARGS__, SYSCALL_CHECK_3, SYSCALL_CHECK_2, SYSCALL_CHECK_1) \
317  (__VA_ARGS__)
320 #define GET_SYSCALL_CHECK_MACRO(_1, _2, _3, NAME, ...) NAME
321 #define SYSCALL_CHECK_1(_return_value) \
322  do { \
323  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value); \
324  } while (0)
325 #define SYSCALL_CHECK_2(_return_value, _extra_msg) \
326  do { \
327  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value, _extra_msg); \
328  } while (0)
329 #define SYSCALL_CHECK_3(_return_value, _extra_msg, _error_value) \
330  do { \
331  kvikio::detail::check_linux_call(__LINE__, __FILE__, _return_value, _extra_msg, _error_value); \
332  } while (0)
333 
334 namespace detail {
335 void handle_linux_call_error(int line_number, char const* filename, std::string_view extra_msg);
336 
337 inline void check_linux_call(int line_number,
338  char const* filename,
339  int return_value,
340  std::string_view extra_msg = "",
341  int error_value = -1)
342 {
343  if (return_value == error_value) { handle_linux_call_error(line_number, filename, extra_msg); }
344 }
345 
346 template <typename T>
347 void check_linux_call(
348  int line_number, char const* filename, T return_value, std::string_view extra_msg, T error_value)
349 {
350  if (return_value == error_value) { handle_linux_call_error(line_number, filename, extra_msg); }
351 }
352 } // namespace detail
353 
354 } // namespace kvikio
#define CUDA_DRIVER_TRY(...)
Error checking macro for CUDA driver API functions.
Definition: error.hpp:57
KvikIO namespace.
Definition: batch.hpp:16