Files
paddlepaddle--paddle/paddle/common/enforce.h
T
2026-07-13 12:40:42 +08:00

417 lines
16 KiB
C++

// 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 <cxxabi.h> // for __cxa_demangle
#endif // __GNUC__
#include <cinttypes>
#include <exception>
#include <iostream>
#if !defined(_WIN32)
#include <dlfcn.h> // dladdr
#include <unistd.h> // sleep, usleep
#else // _WIN32
#ifndef NOMINMAX
#define NOMINMAX // msvc max/min macro conflict with std::min/max
#endif
#include <windows.h> // 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 <execinfo.h>
#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<char, void (*)(void*)> 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 <typename T>
struct CanToString {
private:
using YesType = uint8_t;
using NoType = uint16_t;
template <typename U>
static YesType Check(decltype(std::cout << std::declval<U>())) {
return 0;
}
template <typename U>
static NoType Check(...) {
return 0;
}
public:
static constexpr bool kValue =
std::is_same<YesType, decltype(Check<T>(std::cout))>::value;
};
template <bool kCanToString /* = true */>
struct BinaryCompareMessageConverter {
template <typename T>
static std::string Convert(const char* expression, const T& value) {
return expression + std::string(":") + paddle::string::to_string(value);
}
};
template <>
struct BinaryCompareMessageConverter<false> {
template <typename T>
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 <typename StrType>
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<StrType>(what), file, line)
<< std::endl;
return sout.str();
}
template <typename StrType>
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 <typename T>
inline constexpr bool IsArithmetic() {
return std::is_arithmetic<T>::value;
}
template <typename T1, typename T2, bool kIsArithmetic /* = true */>
struct TypeConverterImpl {
using Type1 = typename std::common_type<T1, T2>::type;
using Type2 = Type1;
};
template <typename T1, typename T2>
struct TypeConverterImpl<T1, T2, false> {
using Type1 = T1;
using Type2 = T2;
};
template <typename T1, typename T2>
struct TypeConverter {
static constexpr bool kIsArithmetic =
IsArithmetic<T1>() && IsArithmetic<T2>();
using Type1 = typename TypeConverterImpl<T1, T2, kIsArithmetic>::Type1;
using Type2 = typename TypeConverterImpl<T1, T2, kIsArithmetic>::Type2;
};
template <typename T1, typename T2>
using CommonType1 = typename std::add_lvalue_reference<
typename std::add_const<typename TypeConverter<T1, T2>::Type1>::type>::type;
template <typename T1, typename T2>
using CommonType2 = typename std::add_lvalue_reference<
typename std::add_const<typename TypeConverter<T1, T2>::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<int>::max(), \
common::errors::InvalidArgument( \
"Tensor dimension %s=%ld exceeds the maximum value " \
"that int can represent (%d).", \
var_name, \
var, \
std::numeric_limits<int>::max()))
namespace details {
template <typename T,
typename std::enable_if<std::is_signed<T>::value, int>::type = 0>
inline bool IsNegativeForUInt32Max(T var) {
return var < 0;
}
template <typename T,
typename std::enable_if<!std::is_signed<T>::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<decltype(__var)>::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<int64_t>(__var))); \
} \
PADDLE_ENFORCE_LE( \
static_cast<uint64_t>(__var), \
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()), \
common::errors::InvalidArgument( \
"The value must fit in uint32_t. Expected %s <= %u, but " \
"received %s = %" PRIu64 ".", \
var_name, \
std::numeric_limits<uint32_t>::max(), \
var_name, \
static_cast<uint64_t>(__var))); \
} while (0)
TEST_API bool RegisterLogSimplyStr(const std::string& type,
const std::string& simply);
template <typename T>
class LogSimplyStrRegistrar {
public:
static bool success;
};
#define REGISTER_LOG_SIMPLY_STR(Type) \
template <> \
bool ::common::enforce::LogSimplyStrRegistrar<Type>::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 <typename T>
inline bool is_error(const T& stat) {
return !stat;
}
namespace pir {
#define IR_THROW(...) PADDLE_THROW(common::errors::Fatal(__VA_ARGS__))
} // namespace pir