// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #ifdef __GNUC__ #include // for __cxa_demangle #endif // __GNUC__ #include #include #include #if !defined(_WIN32) #include // dladdr #include // sleep, usleep #else // _WIN32 #ifndef NOMINMAX #define NOMINMAX // msvc max/min macro conflict with std::min/max #endif #include // GetModuleFileName, Sleep #endif #include "paddle/common/errors.h" #include "paddle/common/macros.h" #include "paddle/utils/test_macros.h" #if !defined(_WIN32) && !defined(PADDLE_WITH_MUSL) #include #endif #include "paddle/utils/string/printf.h" #include "paddle/utils/string/to_string.h" #include "paddle/utils/variant.h" namespace common { #ifdef __GNUC__ inline std::string demangle(std::string name) { int status = -4; // some arbitrary value to eliminate the compiler warning std::unique_ptr res{ abi::__cxa_demangle(name.c_str(), NULL, NULL, &status), std::free}; return (status == 0) ? res.get() : name; } #else inline std::string demangle(std::string name) { return name; } #endif namespace enforce { TEST_API void SkipPaddleFatal(bool skip = true); TEST_API bool IsPaddleFatalSkip(); namespace details { class PaddleFatalGuard { public: PaddleFatalGuard() : skip_paddle_fatal_(IsPaddleFatalSkip()) { if (!skip_paddle_fatal_) SkipPaddleFatal(true); } ~PaddleFatalGuard() { if (!skip_paddle_fatal_) SkipPaddleFatal(false); } private: bool skip_paddle_fatal_; }; template struct CanToString { private: using YesType = uint8_t; using NoType = uint16_t; template static YesType Check(decltype(std::cout << std::declval())) { return 0; } template static NoType Check(...) { return 0; } public: static constexpr bool kValue = std::is_same(std::cout))>::value; }; template struct BinaryCompareMessageConverter { template static std::string Convert(const char* expression, const T& value) { return expression + std::string(":") + paddle::string::to_string(value); } }; template <> struct BinaryCompareMessageConverter { template static const char* Convert(const char* expression, const T& value UNUSED) { return expression; } }; // Note: This Macro can only be used within enforce.h #define __THROW_ERROR_INTERNAL__(__ERROR_SUMMARY) \ do { \ HANDLE_THE_ERROR \ throw ::common::enforce::EnforceNotMet( \ __ERROR_SUMMARY, __FILE__, __LINE__); \ END_HANDLE_THE_ERROR \ } while (0) } // namespace details TEST_API int GetCallStackLevel(); TEST_API std::string SimplifyErrorTypeFormat(const std::string& str); TEST_API std::string GetCurrentTraceBackString(bool for_signal = false); template static std::string GetErrorSummaryString(StrType&& what, const char* file, int line) { std::ostringstream sout; if (GetCallStackLevel() > 1) { sout << "\n----------------------\nError Message " "Summary:\n----------------------\n"; } sout << paddle::string::Sprintf( "%s (at %s:%d)", std::forward(what), file, line) << std::endl; return sout.str(); } template static std::string GetTraceBackString(StrType&& what, const char* file, int line) { if (GetCallStackLevel() > 1) { // FLAGS_call_stack_level>1 means showing c++ call stack return ::common::enforce::GetCurrentTraceBackString() + GetErrorSummaryString(what, file, line); } else { return GetErrorSummaryString(what, file, line); } } struct EnforceNotMet : public std::exception { public: EnforceNotMet(std::exception_ptr e, const char* file, int line) { try { std::rethrow_exception(e); } catch (EnforceNotMet& e) { code_ = e.code(); err_str_ = GetTraceBackString(e.what(), file, line); simple_err_str_ = SimplifyErrorTypeFormat(err_str_); } catch (std::exception& e) { err_str_ = GetTraceBackString(e.what(), file, line); simple_err_str_ = SimplifyErrorTypeFormat(err_str_); } } EnforceNotMet(const std::string& str, const char* file, int line) : err_str_(GetTraceBackString(str, file, line)) { simple_err_str_ = SimplifyErrorTypeFormat(err_str_); } EnforceNotMet(const common::ErrorSummary& error, const char* file, int line) : code_(error.code()), err_str_(GetTraceBackString(error.to_string(), file, line)) { simple_err_str_ = SimplifyErrorTypeFormat(err_str_); } const char* what() const noexcept override { if (GetCallStackLevel() > 1) { return err_str_.c_str(); } else { return simple_err_str_.c_str(); } } common::ErrorCode code() const { return code_; } const std::string& error_str() const { return err_str_; } const std::string& simple_error_str() const { return simple_err_str_; } void set_error_str(std::string str) { if (GetCallStackLevel() > 1) { err_str_ = str; } else { simple_err_str_ = str; } } ~EnforceNotMet() override = default; private: // Used to determine the final type of exception thrown common::ErrorCode code_ = common::ErrorCode::LEGACY; // Complete error message // e.g. InvalidArgumentError: *** std::string err_str_; // Simple error message used when no C++ stack and python compile stack // e.g. (InvalidArgument) *** std::string simple_err_str_; details::PaddleFatalGuard paddle_fatal_guard_; }; /** HELPER MACROS AND FUNCTIONS **/ #ifndef PADDLE_MAY_THROW #define PADDLE_MAY_THROW noexcept(false) #endif #if defined _WIN32 && defined PADDLE_ON_INFERENCE && defined PADDLE_NO_PYTHON #define HANDLE_THE_ERROR try { #define END_HANDLE_THE_ERROR \ } \ catch (const std::exception& e) { \ std::cout << e.what() << std::endl; \ throw; \ } #else #define HANDLE_THE_ERROR #define END_HANDLE_THE_ERROR #endif template inline constexpr bool IsArithmetic() { return std::is_arithmetic::value; } template struct TypeConverterImpl { using Type1 = typename std::common_type::type; using Type2 = Type1; }; template struct TypeConverterImpl { using Type1 = T1; using Type2 = T2; }; template struct TypeConverter { static constexpr bool kIsArithmetic = IsArithmetic() && IsArithmetic(); using Type1 = typename TypeConverterImpl::Type1; using Type2 = typename TypeConverterImpl::Type2; }; template using CommonType1 = typename std::add_lvalue_reference< typename std::add_const::Type1>::type>::type; template using CommonType2 = typename std::add_lvalue_reference< typename std::add_const::Type2>::type>::type; #define PADDLE_THROW(...) \ do { \ HANDLE_THE_ERROR \ throw ::common::enforce::EnforceNotMet( \ ::common::ErrorSummary(__VA_ARGS__), __FILE__, __LINE__); \ END_HANDLE_THE_ERROR \ } while (0) #define PADDLE_FATAL(...) \ if (!::common::enforce::IsPaddleFatalSkip()) { \ auto info = ::common::enforce::EnforceNotMet( \ paddle::string::Sprintf(__VA_ARGS__), __FILE__, __LINE__); \ std::cerr << info.what() << std::endl; \ std::abort(); \ } #define __PADDLE_BINARY_COMPARE(__VAL1, __VAL2, __CMP, __INV_CMP, ...) \ do { \ auto __val1 = (__VAL1); \ auto __val2 = (__VAL2); \ using __TYPE1__ = decltype(__val1); \ using __TYPE2__ = decltype(__val2); \ using __COMMON_TYPE1__ = \ ::common::enforce::CommonType1<__TYPE1__, __TYPE2__>; \ using __COMMON_TYPE2__ = \ ::common::enforce::CommonType2<__TYPE1__, __TYPE2__>; \ bool __is_not_error = (static_cast<__COMMON_TYPE1__>(__val1))__CMP( \ static_cast<__COMMON_TYPE2__>(__val2)); \ if (UNLIKELY(!__is_not_error)) { \ auto __summary__ = ::common::ErrorSummary(__VA_ARGS__); \ constexpr bool __kCanToString__ = \ ::common::details::CanToString<__TYPE1__>::kValue && \ ::common::details::CanToString<__TYPE2__>::kValue; \ auto __message__ = ::paddle::string::Sprintf( \ "%s\n [Hint: Expected %s " #__CMP \ " %s, but received %s " #__INV_CMP " %s.]", \ __summary__.error_message(), \ #__VAL1, \ #__VAL2, \ ::common::details::BinaryCompareMessageConverter< \ __kCanToString__>::Convert(#__VAL1, __val1), \ ::common::details::BinaryCompareMessageConverter< \ __kCanToString__>::Convert(#__VAL2, __val2)); \ __THROW_ERROR_INTERNAL__( \ ::common::ErrorSummary(__summary__.code(), std::move(__message__))); \ } \ } while (0) #define PADDLE_ENFORCE_NOT_NULL(__VAL, ...) \ do { \ if (UNLIKELY(nullptr == (__VAL))) { \ auto __summary__ = ::common::ErrorSummary(__VA_ARGS__); \ auto __message__ = ::paddle::string::Sprintf( \ "%s\n [Hint: " #__VAL " should not be null.]", \ __summary__.error_message()); \ __THROW_ERROR_INTERNAL__( \ ::common::ErrorSummary(__summary__.code(), std::move(__message__))); \ } \ } while (0) #define PADDLE_ENFORCE_EQ(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, ==, !=, __VA_ARGS__) #define PADDLE_ENFORCE_NE(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, !=, ==, __VA_ARGS__) #define PADDLE_ENFORCE_GT(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, >, <=, __VA_ARGS__) #define PADDLE_ENFORCE_GE(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, >=, <, __VA_ARGS__) #define PADDLE_ENFORCE_LT(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <, >=, __VA_ARGS__) #define PADDLE_ENFORCE_LE(__VAL0, __VAL1, ...) \ __PADDLE_BINARY_COMPARE(__VAL0, __VAL1, <=, >, __VA_ARGS__) #define PADDLE_ENFORCE_LE_INT_MAX(var, var_name) \ PADDLE_ENFORCE_LE(var, \ std::numeric_limits::max(), \ common::errors::InvalidArgument( \ "Tensor dimension %s=%ld exceeds the maximum value " \ "that int can represent (%d).", \ var_name, \ var, \ std::numeric_limits::max())) namespace details { template ::value, int>::type = 0> inline bool IsNegativeForUInt32Max(T var) { return var < 0; } template ::value, int>::type = 0> inline bool IsNegativeForUInt32Max(T var UNUSED) { return false; } } // namespace details #define PADDLE_ENFORCE_LE_UINT32_MAX(var, var_name) \ do { \ auto __var = (var); \ using __VAR_TYPE__ = typename std::decay::type; \ static_assert(std::is_integral<__VAR_TYPE__>::value, \ "PADDLE_ENFORCE_LE_UINT32_MAX only accepts integral " \ "types."); \ if (::common::enforce::details::IsNegativeForUInt32Max(__var)) { \ PADDLE_THROW(common::errors::InvalidArgument( \ "The value must be non-negative. Expected %s >= 0, but " \ "received %s = %" PRId64 ".", \ var_name, \ var_name, \ static_cast(__var))); \ } \ PADDLE_ENFORCE_LE( \ static_cast(__var), \ static_cast(std::numeric_limits::max()), \ common::errors::InvalidArgument( \ "The value must fit in uint32_t. Expected %s <= %u, but " \ "received %s = %" PRIu64 ".", \ var_name, \ std::numeric_limits::max(), \ var_name, \ static_cast(__var))); \ } while (0) TEST_API bool RegisterLogSimplyStr(const std::string& type, const std::string& simply); template class LogSimplyStrRegistrar { public: static bool success; }; #define REGISTER_LOG_SIMPLY_STR(Type) \ template <> \ bool ::common::enforce::LogSimplyStrRegistrar::success = \ ::common::enforce::RegisterLogSimplyStr( \ ::common::demangle(typeid(Type).name()), #Type); } // namespace enforce using namespace enforce; // NOLINT } // namespace common // TODO(zhangbopd): This is a copy from pir, and should be removed after merge // this into common enforce namespace above. template inline bool is_error(const T& stat) { return !stat; } namespace pir { #define IR_THROW(...) PADDLE_THROW(common::errors::Fatal(__VA_ARGS__)) } // namespace pir