#ifndef C10_UTIL_EXCEPTION_H_ #define C10_UTIL_EXCEPTION_H_ #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER <= 1900 #define __func__ __FUNCTION__ #endif namespace c10 { /// The primary ATen error class. /// Provides a complete error message with source location information via /// `what()`, and a more concise message via `what_without_backtrace()`. /// Don't throw this directly; use TORCH_CHECK/TORCH_INTERNAL_ASSERT instead. /// /// NB: c10::Error is handled specially by the default torch to suppress the /// backtrace, see torch/csrc/Exceptions.h class C10_API Error : public std::exception { // The actual error message. std::string msg_; // Context for the message (in order of decreasing specificity). Context will // be automatically formatted appropriately, so it is not necessary to add // extra leading/trailing newlines to strings inside this vector std::vector context_; // The C++ backtrace at the point when this exception was raised. This // may be empty if there is no valid backtrace. (We don't use optional // here to reduce the dependencies this file has.) std::string backtrace_; // These two are derived fields from msg_stack_ and backtrace_, but we need // fields for the strings so that we can return a const char* (as the // signature of std::exception requires). Currently, the invariant // is that these fields are ALWAYS populated consistently with respect // to msg_stack_ and backtrace_. std::string what_; std::string what_without_backtrace_; // This is a little debugging trick: you can stash a relevant pointer // in caller, and then when you catch the exception, you can compare // against pointers you have on hand to get more information about // where the exception came from. In Caffe2, this is used to figure // out which operator raised an exception. const void* caller_; public: // PyTorch-style Error constructor. NB: the implementation of this // is actually in Logging.cpp Error(SourceLocation source_location, std::string msg); // Caffe2-style error message Error( const char* file, const uint32_t line, const char* condition, const std::string& msg, const std::string& backtrace, const void* caller = nullptr); // Base constructor Error(std::string msg, std::string backtrace, const void* caller = nullptr); // Add some new context to the message stack. The last added context // will be formatted at the end of the context list upon printing. // WARNING: This method is O(n) in the size of the stack, so don't go // wild adding a ridiculous amount of context to error messages. void add_context(std::string msg); const std::string& msg() const { return msg_; } const std::vector& context() const { return context_; } const std::string& backtrace() const { return backtrace_; } /// Returns the complete error message, including the source location. /// The returned pointer is invalidated if you call add_context() on /// this object. const char* what() const noexcept override { return what_.c_str(); } const void* caller() const noexcept { return caller_; } /// Returns only the error message string, without source location. /// The returned pointer is invalidated if you call add_context() on /// this object. const char* what_without_backtrace() const noexcept { return what_without_backtrace_.c_str(); } private: void refresh_what(); std::string compute_what(bool include_backtrace) const; }; class C10_API WarningHandler { public: virtual ~WarningHandler() = default; /// The default warning handler. Prints the message to stderr. virtual void process( const SourceLocation& source_location, const std::string& msg, const bool verbatim); }; namespace Warning { // Note: [Verbatim Warnings] // Warnings originating in C++ code can appear out-of-place to Python users: // a user runs a line in Python, but the warning references a line in C++. // Some parts of PyTorch, like the JIT, are cognizant of this mismatch // and take care to map warnings back to the user's program, but most // of PyTorch simply throws a context-free warning. To allow warning // handlers to add context where appropriate, warn takes the // "verbatim" flag. When this is false a warning handler might append // the C++ warning to a Python warning message that relates the warning // back to the user's program. Callers who have already accounted for // context in their warnings should set verbatim to true so their warnings // appear without modification. /// Issue a warning with a given message. Dispatched to the current /// warning handler. C10_API void warn( const SourceLocation& source_location, const std::string& msg, bool verbatim); C10_API void warn( SourceLocation source_location, const char* msg, bool verbatim); C10_API void warn( SourceLocation source_location, ::c10::detail::CompileTimeEmptyString msg, bool verbatim); /// Sets the global warning handler. This is not thread-safe, so it should /// generally be called once during initialization or while holding the GIL /// for programs that use python. /// User is responsible for keeping the WarningHandler alive until /// it is not needed. C10_API void set_warning_handler(WarningHandler* handler) noexcept(true); /// Gets the global warning handler. C10_API WarningHandler* get_warning_handler() noexcept(true); class C10_API WarningHandlerGuard { WarningHandler* prev_handler_; public: WarningHandlerGuard(WarningHandler* new_handler) : prev_handler_(c10::Warning::get_warning_handler()) { c10::Warning::set_warning_handler(new_handler); } ~WarningHandlerGuard() { c10::Warning::set_warning_handler(prev_handler_); } }; /// The TORCH_WARN_ONCE macro is difficult to test for. Use /// setWarnAlways(true) to turn it into TORCH_WARN, which can be /// tested for more easily. C10_API void set_warnAlways(bool) noexcept(true); C10_API bool get_warnAlways(void) noexcept(true); // A RAII guard that sets warn_always (not thread-local) on // construction, and sets it back to the original value upon destruction. struct C10_API WarnAlways { public: explicit WarnAlways(bool setting = true); ~WarnAlways(); private: bool prev_setting; }; } // namespace Warning // Used in ATen for out-of-bound indices that can reasonably only be detected // lazily inside a kernel (See: advanced indexing). These turn into // IndexError when they cross to Python. class C10_API IndexError : public Error { using Error::Error; }; // Used in ATen for invalid values. These turn into // ValueError when they cross to Python. class C10_API ValueError : public Error { using Error::Error; }; // Used in ATen for invalid types. These turn into // TypeError when they cross to Python. class C10_API TypeError : public Error { using Error::Error; }; // Used in ATen for functionality that is not implemented. These turn into // NotImplementedError when they cross to Python. class C10_API NotImplementedError : public Error { using Error::Error; }; // Used in ATen for non finite indices. These turn into // ExitException when they cross to Python. class C10_API EnforceFiniteError : public Error { using Error::Error; }; // Used in Onnxifi backend lowering. These turn into // ExitException when they cross to Python. class C10_API OnnxfiBackendSystemError : public Error { using Error::Error; }; // Used for numerical errors from the linalg module. These // turn into LinAlgError when they cross into Python. class C10_API LinAlgError : public Error { using Error::Error; }; // A utility function to return an exception std::string by prepending its // exception type before its what() content C10_API std::string GetExceptionString(const std::exception& e); } // namespace c10 // Private helper macro for implementing TORCH_INTERNAL_ASSERT and TORCH_CHECK // // Note: In the debug build With MSVC, __LINE__ might be of long type (a.k.a // int32_t), which is different from the definition of `SourceLocation` that // requires unsigned int (a.k.a uint32_t) and may cause a compile error with the // message: error C2397: conversion from 'long' to 'uint32_t' requires a // narrowing conversion Here the static cast is used to pass the build. if this // is used inside a lambda the __func__ macro expands to operator(), which isn't // very useful, but hard to fix in a macro so suppressing the warning. #define C10_THROW_ERROR(err_type, msg) \ throw ::c10::err_type( \ {__func__, __FILE__, static_cast(__LINE__)}, msg) // Private helper macro for workaround MSVC misexpansion of nested macro // invocations involving __VA_ARGS__. See // https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly #define C10_EXPAND_MSVC_WORKAROUND(x) x // On nvcc, C10_UNLIKELY thwarts missing return statement analysis. In cases // where the unlikely expression may be a constant, use this macro to ensure // return statement analysis keeps working (at the cost of not getting the // likely/unlikely annotation on nvcc). // https://github.com/pytorch/pytorch/issues/21418 // // Currently, this is only used in the error reporting macros below. If you // want to use it more generally, move me to Macros.h // // TODO: Brian Vaughan observed that we might be able to get this to work on // nvcc by writing some sort of C++ overload that distinguishes constexpr inputs // from non-constexpr. Since there isn't any evidence that losing C10_UNLIKELY // in nvcc is causing us perf problems, this is not yet implemented, but this // might be an interesting piece of C++ code for an intrepid bootcamper to // write. #if defined(__CUDACC__) #define C10_UNLIKELY_OR_CONST(e) e #else #define C10_UNLIKELY_OR_CONST(e) C10_UNLIKELY(e) #endif // ---------------------------------------------------------------------------- // Error reporting macros // ---------------------------------------------------------------------------- #ifdef STRIP_ERROR_MESSAGES #define TORCH_RETHROW(e, ...) throw #else #define TORCH_RETHROW(e, ...) \ do { \ e.add_context(::c10::str(__VA_ARGS__)); \ throw; \ } while (false) #endif // A utility macro to provide assert()-like functionality; that is, enforcement // of internal invariants in code. It supports an arbitrary number of extra // arguments (evaluated only on failure), which will be printed in the assert // failure message using operator<< (this is useful to print some variables // which may be useful for debugging.) // // Usage: // TORCH_INTERNAL_ASSERT(should_be_true); // TORCH_INTERNAL_ASSERT(x == 0, "x = ", x); // // Assuming no bugs in PyTorch, the conditions tested by this macro should // always be true; e.g., it should be possible to disable all of these // conditions without changing observable user behavior. If you would like to // do error reporting for user input, please use TORCH_CHECK instead. // // NOTE: It is SAFE to use this macro in production code; on failure, this // simply raises an exception, it does NOT unceremoniously quit the process // (unlike assert()). // #ifdef STRIP_ERROR_MESSAGES #define TORCH_INTERNAL_ASSERT(cond, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ ::c10::detail::torchCheckFail( \ __func__, \ __FILE__, \ static_cast(__LINE__), \ #cond "INTERNAL ASSERT FAILED at" C10_STRINGIZE(__FILE__)); \ } #else // It would be nice if we could build a combined string literal out of // the TORCH_INTERNAL_ASSERT prefix and a user-provided string literal // as the first argument, but there doesn't seem to be any good way to // do that while still supporting having a first argument that isn't a // string literal. #define TORCH_INTERNAL_ASSERT(cond, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ ::c10::detail::torchInternalAssertFail( \ __func__, \ __FILE__, \ static_cast(__LINE__), \ #cond \ "INTERNAL ASSERT FAILED at " C10_STRINGIZE(__FILE__) ":" C10_STRINGIZE( \ __LINE__) ", please report a bug to PyTorch. ", \ c10::str(__VA_ARGS__)); \ } #endif // A utility macro to make it easier to test for error conditions from user // input. Like TORCH_INTERNAL_ASSERT, it supports an arbitrary number of extra // arguments (evaluated only on failure), which will be printed in the error // message using operator<< (e.g., you can pass any object which has // operator<< defined. Most objects in PyTorch have these definitions!) // // Usage: // TORCH_CHECK(should_be_true); // A default error message will be provided // // in this case; but we recommend writing an // // explicit error message, as it is more // // user friendly. // TORCH_CHECK(x == 0, "Expected x to be 0, but got ", x); // // On failure, this macro will raise an exception. If this exception propagates // to Python, it will convert into a Python RuntimeError. // // NOTE: It is SAFE to use this macro in production code; on failure, this // simply raises an exception, it does NOT unceremoniously quit the process // (unlike CHECK() from glog.) // #define TORCH_CHECK_WITH(error_t, cond, ...) \ TORCH_CHECK_WITH_MSG(error_t, cond, "", __VA_ARGS__) #ifdef STRIP_ERROR_MESSAGES #define TORCH_CHECK_MSG(cond, type, ...) \ (#cond #type " CHECK FAILED at " C10_STRINGIZE(__FILE__)) #define TORCH_CHECK_WITH_MSG(error_t, cond, type, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ C10_THROW_ERROR(Error, TORCH_CHECK_MSG(cond, type, __VA_ARGS__)); \ } #else namespace c10 { namespace detail { template decltype(auto) torchCheckMsgImpl(const char* msg, const Args&... args) { return ::c10::str(args...); } inline C10_API const char* torchCheckMsgImpl(const char* msg) { return msg; } // If there is just 1 user-provided C-string argument, use it. inline C10_API const char* torchCheckMsgImpl( const char* msg, const char* args) { return args; } } // namespace detail } // namespace c10 #define TORCH_CHECK_MSG(cond, type, ...) \ (::c10::detail::torchCheckMsgImpl( \ "Expected " #cond \ " to be true, but got false. " \ "(Could this error message be improved? If so, " \ "please report an enhancement request to PyTorch.)", \ ##__VA_ARGS__)) #define TORCH_CHECK_WITH_MSG(error_t, cond, type, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ C10_THROW_ERROR(error_t, TORCH_CHECK_MSG(cond, type, __VA_ARGS__)); \ } #endif namespace c10 { namespace detail { [[noreturn]] C10_API void torchCheckFail( const char* func, const char* file, uint32_t line, const std::string& msg); [[noreturn]] C10_API void torchCheckFail( const char* func, const char* file, uint32_t line, const char* msg); // The c10::str() call that creates userMsg can have 1 of 3 return // types depending on the number and types of arguments passed to // TORCH_INTERNAL_ASSERT. 0 arguments will get a // CompileTimeEmptyString, 1 const char * will be passed straight // through, and anything else will get converted to std::string. [[noreturn]] C10_API void torchInternalAssertFail( const char* func, const char* file, uint32_t line, const char* condMsg, const char* userMsg); [[noreturn]] inline C10_API void torchInternalAssertFail( const char* func, const char* file, uint32_t line, const char* condMsg, ::c10::detail::CompileTimeEmptyString userMsg) { torchCheckFail(func, file, line, condMsg); } [[noreturn]] C10_API void torchInternalAssertFail( const char* func, const char* file, uint32_t line, const char* condMsg, const std::string& userMsg); } // namespace detail } // namespace c10 #ifdef STRIP_ERROR_MESSAGES #define TORCH_CHECK(cond, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ ::c10::detail::torchCheckFail( \ __func__, \ __FILE__, \ static_cast(__LINE__), \ TORCH_CHECK_MSG(cond, "", __VA_ARGS__)); \ } #else #define TORCH_CHECK(cond, ...) \ if (C10_UNLIKELY_OR_CONST(!(cond))) { \ ::c10::detail::torchCheckFail( \ __func__, \ __FILE__, \ static_cast(__LINE__), \ TORCH_CHECK_MSG(cond, "", ##__VA_ARGS__)); \ } #endif // An utility macro that does what `TORCH_CHECK` does if compiled in the host // code, otherwise does nothing. Supposed to be used in the code shared between // host and device code as an alternative for `TORCH_CHECK`. #if defined(__CUDACC__) || defined(__HIPCC__) #define TORCH_CHECK_IF_NOT_ON_CUDA(cond, ...) #else #define TORCH_CHECK_IF_NOT_ON_CUDA(cond, ...) TORCH_CHECK(cond, ##__VA_ARGS__) #endif // Debug only version of TORCH_INTERNAL_ASSERT. This macro only checks in debug // build, and does nothing in release build. It is appropriate to use // in situations where you want to add an assert to a hotpath, but it is // too expensive to run this assert on production builds. #ifdef NDEBUG // Optimized version - generates no code. #define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) \ while (false) \ C10_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(__VA_ARGS__)) #else #define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) \ C10_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(__VA_ARGS__)) #endif // TODO: We're going to get a lot of similar looking string literals // this way; check if this actually affects binary size. // Like TORCH_CHECK, but raises LinAlgError instead of Error. #define TORCH_CHECK_LINALG(cond, ...) \ TORCH_CHECK_WITH_MSG(LinAlgError, cond, "LINALG", __VA_ARGS__) // Like TORCH_CHECK, but raises IndexErrors instead of Errors. #define TORCH_CHECK_INDEX(cond, ...) \ TORCH_CHECK_WITH_MSG(IndexError, cond, "INDEX", __VA_ARGS__) // Like TORCH_CHECK, but raises ValueErrors instead of Errors. #define TORCH_CHECK_VALUE(cond, ...) \ TORCH_CHECK_WITH_MSG(ValueError, cond, "VALUE", __VA_ARGS__) // Like TORCH_CHECK, but raises TypeErrors instead of Errors. #define TORCH_CHECK_TYPE(cond, ...) \ TORCH_CHECK_WITH_MSG(TypeError, cond, "TYPE", __VA_ARGS__) // Like TORCH_CHECK, but raises NotImplementedErrors instead of Errors. #define TORCH_CHECK_NOT_IMPLEMENTED(cond, ...) \ TORCH_CHECK_WITH_MSG(NotImplementedError, cond, "TYPE", __VA_ARGS__) // Report a warning to the user. Accepts an arbitrary number of extra // arguments which are concatenated into the warning message using operator<< // #ifdef STRIP_ERROR_MESSAGES #define TORCH_WARN(...) \ ::c10::Warning::warn( \ {__func__, __FILE__, static_cast(__LINE__)}, \ ::c10::detail::CompileTimeEmptyString{}, \ false) #else #define TORCH_WARN(...) \ ::c10::Warning::warn( \ {__func__, __FILE__, static_cast(__LINE__)}, \ ::c10::str(__VA_ARGS__), \ false) #endif // Report a warning to the user only once. Accepts an arbitrary number of extra // arguments which are concatenated into the warning message using operator<< // #ifdef STRIP_ERROR_MESSAGES #define _TORCH_WARN_ONCE(...) \ C10_UNUSED static const auto C10_ANONYMOUS_VARIABLE(torch_warn_once_) = \ [&] { \ ::c10::Warning::warn( \ {__func__, __FILE__, static_cast(__LINE__)}, \ ::c10::detail::CompileTimeEmptyString{}, \ false); \ return true; \ }() #else #define _TORCH_WARN_ONCE(...) \ C10_UNUSED static const auto C10_ANONYMOUS_VARIABLE(torch_warn_once_) = \ [&] { \ ::c10::Warning::warn( \ {__func__, __FILE__, static_cast(__LINE__)}, \ ::c10::str(__VA_ARGS__), \ false); \ return true; \ }() #endif #define TORCH_WARN_ONCE(...) \ if (::c10::Warning::get_warnAlways()) { \ TORCH_WARN(__VA_ARGS__); \ } else { \ _TORCH_WARN_ONCE(__VA_ARGS__); \ } // Report an error with a specific argument // NOTE: using the argument name in TORCH_CHECK's message is preferred #define TORCH_CHECK_ARG(cond, argN, ...) \ TORCH_CHECK(cond, "invalid argument ", argN, ": ", __VA_ARGS__) // ---------------------------------------------------------------------------- // Deprecated macros // ---------------------------------------------------------------------------- namespace c10 { namespace detail { /* // Deprecation disabled until we fix sites in our codebase C10_DEPRECATED_MESSAGE("AT_ERROR(msg) is deprecated, use TORCH_CHECK(false, msg) instead.") */ inline void deprecated_AT_ERROR() {} /* // Deprecation disabled until we fix sites in our codebase C10_DEPRECATED_MESSAGE("AT_ASSERT is deprecated, if you mean to indicate an internal invariant failure, use " \ "TORCH_INTERNAL_ASSERT instead; if you mean to do user error checking, use " \ "TORCH_CHECK. See https://github.com/pytorch/pytorch/issues/20287 for more details.") */ inline void deprecated_AT_ASSERT() {} /* // Deprecation disabled until we fix sites in our codebase C10_DEPRECATED_MESSAGE("AT_ASSERTM is deprecated, if you mean to indicate an internal invariant failure, use " \ "TORCH_INTERNAL_ASSERT instead; if you mean to do user error checking, use " \ "TORCH_CHECK. See https://github.com/pytorch/pytorch/issues/20287 for more details.") */ inline void deprecated_AT_ASSERTM() {} } // namespace detail } // namespace c10 // Deprecated alias; this alias was deprecated because people kept mistakenly // using it for user error checking. Use TORCH_INTERNAL_ASSERT or TORCH_CHECK // instead. See https://github.com/pytorch/pytorch/issues/20287 for more // details. #define AT_ASSERT(...) \ do { \ ::c10::detail::deprecated_AT_ASSERT(); \ C10_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(__VA_ARGS__)); \ } while (false) // Deprecated alias, like AT_ASSERT. The new TORCH_INTERNAL_ASSERT macro // supports both 0-ary and variadic calls, so having a separate // message-accepting macro is not necessary. // // NB: we MUST include cond explicitly here, as MSVC will miscompile the macro // expansion, shunting all of __VA_ARGS__ to cond. An alternate workaround // can be seen at // https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly #define AT_ASSERTM(cond, ...) \ do { \ ::c10::detail::deprecated_AT_ASSERTM(); \ C10_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(cond, __VA_ARGS__)); \ } while (false) // Deprecated alias; this alias was deprecated because it represents extra API // surface that makes it hard for people to understand what macro to use. // Use TORCH_CHECK(false, ...) or TORCH_INTERNAL_ASSERT(false, ...) to // unconditionally fail at a line of code. #define AT_ERROR(...) \ do { \ ::c10::detail::deprecated_AT_ERROR(); \ C10_EXPAND_MSVC_WORKAROUND(TORCH_CHECK(false, ::c10::str(__VA_ARGS__))); \ } while (false) #endif // C10_UTIL_EXCEPTION_H_