301 lines
11 KiB
C++
301 lines
11 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#if (defined(__AUTORTFM) && __AUTORTFM)
|
|
|
|
#include "AutoRTFM.h"
|
|
#include "ExternAPI.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdarg>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
// If 0 then verbose logging is compiled-out.
|
|
#define AUTORTFM_VERBOSE_ENABLED 0
|
|
|
|
namespace AutoRTFM
|
|
{
|
|
|
|
// Reports an internal AutoRTFM issue. The behavior of this function will vary
|
|
// based on ForTheRuntime::GetInternalAbortAction() and
|
|
// ForTheRuntime::GetEnsureOnInternalAbort().
|
|
// If ProgramCounter is null, then the return address of the call will be used.
|
|
__attribute__((__format__(__printf__, 4, 5)))
|
|
UE_AUTORTFM_API void ReportError(const char* File, int Line, void* ProgramCounter, const char* Format, ...);
|
|
|
|
[[noreturn]] inline void InternalUnreachable(); //-V1082 Silence PVS [[noreturn]] false positive warning
|
|
|
|
std::string GetFunctionDescription(void* FunctionPtr);
|
|
|
|
template<typename TReturnType, typename... TParameterTypes>
|
|
std::string GetFunctionDescription(TReturnType (*FunctionPtr)(TParameterTypes...))
|
|
{
|
|
return GetFunctionDescription(reinterpret_cast<void*>(FunctionPtr));
|
|
}
|
|
|
|
// We use a special calling convention for this call to minimize
|
|
// the cost of calling this function (which is super unlikely to
|
|
// be called because it is the asserting true codepath, EG. only
|
|
// fatal process destroying explosions happen here).
|
|
[[clang::preserve_most]]
|
|
[[noreturn]]
|
|
UE_AUTORTFM_FORCENOINLINE inline void DoAssert(void(*Logger)()) //-V1082 Silence PVS [[noreturn]] false positive warning
|
|
{
|
|
Logger();
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
// We use a special calling convention for this call to minimize
|
|
// the cost of calling this function (which is super unlikely to
|
|
// be called because it is the expecting true codepath, EG. only
|
|
// things we don't expect to happen get here).
|
|
[[clang::preserve_most]]
|
|
UE_AUTORTFM_FORCENOINLINE inline void DoExpect(void(*Logger)())
|
|
{
|
|
Logger();
|
|
}
|
|
|
|
// Logs a message using a printf-style format string and va_list arguments.
|
|
inline void LogV(const char* File, int Line, void* ProgramCounter, autortfm_log_severity Severity, const char* Format, va_list Args)
|
|
{
|
|
if (ProgramCounter == nullptr)
|
|
{
|
|
ProgramCounter = __builtin_return_address(0);
|
|
}
|
|
|
|
GExternAPI.Log(File, Line, ProgramCounter, Severity, Format, Args);
|
|
}
|
|
|
|
// Logs a message using a printf-style format string and variadic arguments.
|
|
__attribute__((__format__(__printf__, 5, 6)))
|
|
inline void Log(const char* File, int Line, void* ProgramCounter, autortfm_log_severity Severity, const char* Format, ...)
|
|
{
|
|
if (ProgramCounter == nullptr)
|
|
{
|
|
ProgramCounter = __builtin_return_address(0);
|
|
}
|
|
|
|
va_list Args;
|
|
va_start(Args, Format);
|
|
GExternAPI.Log(File, Line, ProgramCounter, Severity, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Logs a message with a callstack using a printf-style format string and va_list arguments.
|
|
inline void LogWithCallstackV(autortfm_log_severity Severity, const char* Format, va_list Args)
|
|
{
|
|
GExternAPI.LogWithCallstack(__builtin_return_address(0), Severity, Format, Args);
|
|
}
|
|
|
|
// Logs a message with a callstack using a printf-style format string and variadic arguments.
|
|
__attribute__((__format__(__printf__, 2, 3)))
|
|
inline void LogWithCallstack(autortfm_log_severity Severity, const char* Format, ...)
|
|
{
|
|
va_list Args;
|
|
va_start(Args, Format);
|
|
GExternAPI.LogWithCallstack(__builtin_return_address(0), Severity, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Reports an ensure failure using a printf-style format string and va_list arguments.
|
|
// If ProgramCounter is null, then the return address of the call will be used.
|
|
inline void EnsureFailureV(const char* File, int Line, void* ProgramCounter, const char* Condition, const char* Format, va_list Args)
|
|
{
|
|
if (ProgramCounter == nullptr)
|
|
{
|
|
ProgramCounter = __builtin_return_address(0);
|
|
}
|
|
|
|
GExternAPI.EnsureFailure(File, Line, ProgramCounter, Condition, Format, Args);
|
|
}
|
|
|
|
// Reports an ensure failure using a printf-style format string and variadic arguments.
|
|
// If ProgramCounter is null, then the return address of the call will be used.
|
|
__attribute__((__format__(__printf__, 5, 6)))
|
|
inline void EnsureFailure(const char* File, int Line, void* ProgramCounter, const char* Condition, const char* Format, ...)
|
|
{
|
|
if (ProgramCounter == nullptr)
|
|
{
|
|
ProgramCounter = __builtin_return_address(0);
|
|
}
|
|
|
|
va_list Args;
|
|
va_start(Args, Format);
|
|
GExternAPI.EnsureFailure(File, Line, ProgramCounter, Condition, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Rounds Value up to the next multiple of Alignment.
|
|
// Alignment must be a power of two.
|
|
template<typename T>
|
|
inline constexpr T AlignDown(T Value, T Alignment)
|
|
{
|
|
static_assert(std::is_integral_v<T>);
|
|
return Value & ~(Alignment - 1);
|
|
}
|
|
|
|
// Rounds Value down to the next multiple of Alignment.
|
|
// Alignment must be a power of two.
|
|
template<typename T>
|
|
inline constexpr T AlignUp(T Value, T Alignment)
|
|
{
|
|
static_assert(std::is_integral_v<T>);
|
|
return (Value + Alignment - 1) & ~(Alignment - 1);
|
|
}
|
|
|
|
// Rounds Value up to the next multiple of Multiple.
|
|
// Unlike AlignDown(), Multiple does not need to be a power of two.
|
|
template<typename T>
|
|
inline constexpr T RoundDown(T Value, T Multiple)
|
|
{
|
|
static_assert(std::is_integral_v<T>);
|
|
return (Value / Multiple) * Multiple;
|
|
}
|
|
|
|
// Rounds Value down to the next multiple of Multiple.
|
|
// Unlike AlignUp(), Multiple does not need to be a power of two.
|
|
template<typename T>
|
|
inline constexpr T RoundUp(T Value, T Multiple)
|
|
{
|
|
static_assert(std::is_integral_v<T>);
|
|
return RoundDown(Value + Multiple - 1, Multiple);
|
|
}
|
|
|
|
// Returns the linear interpolation between Start and End using the coefficient Fraction.
|
|
template<typename T>
|
|
inline constexpr T Lerp(T Start, T End, T Fraction)
|
|
{
|
|
return Start + Fraction * (End - Start);
|
|
}
|
|
|
|
} // namespace AutoRTFM
|
|
|
|
#define AUTORTFM_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
#define AUTORTFM_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
|
|
|
#define AUTORTFM_REQUIRE_SEMICOLON static_assert(true)
|
|
|
|
#define AUTORTFM_REPORT_ERROR(Format, ...) ::AutoRTFM::ReportError(__FILE__, __LINE__, /* ProgramCounter */ nullptr, Format, ##__VA_ARGS__)
|
|
|
|
#define AUTORTFM_VERBOSE(Format, ...) ::AutoRTFM::Log(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_verbose, Format, ##__VA_ARGS__)
|
|
#define AUTORTFM_LOG(Format, ...) ::AutoRTFM::Log(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_info, Format, ##__VA_ARGS__)
|
|
#define AUTORTFM_WARN(Format, ...) ::AutoRTFM::Log(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_warn, Format, ##__VA_ARGS__)
|
|
#define AUTORTFM_ERROR(Format, ...) ::AutoRTFM::Log(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_error, Format, ##__VA_ARGS__)
|
|
#define AUTORTFM_FATAL(Format, ...) ::AutoRTFM::Log(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_fatal, Format, ##__VA_ARGS__)
|
|
|
|
#define AUTORTFM_VERBOSE_V(Format, Args) ::AutoRTFM::LogV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_verbose, Format, Args)
|
|
#define AUTORTFM_LOG_V(Format, Args) ::AutoRTFM::LogV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_info, Format, Args)
|
|
#define AUTORTFM_WARN_V(Format, Args) ::AutoRTFM::LogV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_warn, Format, Args)
|
|
#define AUTORTFM_ERROR_V(Format, Args) ::AutoRTFM::LogV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_error, Format, Args)
|
|
#define AUTORTFM_FATAL_V(Format, Args) ::AutoRTFM::LogV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, autortfm_log_fatal, Format, Args)
|
|
|
|
#define AUTORTFM_VERBOSE_IF(Condition, Format, ...) do { if (Condition) { AUTORTFM_VERBOSE(Format, ##__VA_ARGS__); } } while(false)
|
|
#define AUTORTFM_LOG_IF(Condition, Format, ...) do { if (Condition) { AUTORTFM_LOG(Format, ##__VA_ARGS__); } } while(false)
|
|
#define AUTORTFM_WARN_IF(Condition, Format, ...) do { if (Condition) { AUTORTFM_WARN(Format, ##__VA_ARGS__); } } while(false)
|
|
#define AUTORTFM_ERROR_IF(Condition, Format, ...) do { if (AUTORTFM_UNLIKELY(Condition)) { AUTORTFM_ERROR(Format, ##__VA_ARGS__); } } while(false)
|
|
#define AUTORTFM_FATAL_IF(Condition, Format, ...) do { if (AUTORTFM_UNLIKELY(Condition)) { AUTORTFM_FATAL(Format, ##__VA_ARGS__); } } while(false)
|
|
|
|
// If AUTORTFM_VERBOSE_ENABLED is 0, then replace all the verbose logging macros
|
|
// with no-ops.
|
|
#if !AUTORTFM_VERBOSE_ENABLED
|
|
#undef AUTORTFM_VERBOSE
|
|
#undef AUTORTFM_VERBOSE_V
|
|
#undef AUTORTFM_VERBOSE_IF
|
|
#define AUTORTFM_VERBOSE(...) AUTORTFM_REQUIRE_SEMICOLON
|
|
#define AUTORTFM_VERBOSE_V(...) AUTORTFM_REQUIRE_SEMICOLON
|
|
#define AUTORTFM_VERBOSE_IF(...) AUTORTFM_REQUIRE_SEMICOLON
|
|
#endif
|
|
|
|
#define AUTORTFM_ENSURE(Condition) do { \
|
|
if (AUTORTFM_UNLIKELY(!(Condition))) { \
|
|
[[maybe_unused]] static bool bCalled = [] { \
|
|
::AutoRTFM::EnsureFailure(__FILE__, __LINE__, /* ProgramCounter */ nullptr, #Condition, nullptr); \
|
|
return true; \
|
|
}(); \
|
|
} } while(false)
|
|
|
|
#define AUTORTFM_ENSURE_MSG(Condition, Format, ...) do { \
|
|
if (AUTORTFM_UNLIKELY(!(Condition))) { \
|
|
[[maybe_unused]] static bool bCalled = [&] { \
|
|
::AutoRTFM::EnsureFailure(__FILE__, __LINE__, /* ProgramCounter */ nullptr, #Condition, Format, ##__VA_ARGS__); \
|
|
return true; \
|
|
}(); \
|
|
} } while(false)
|
|
|
|
#define AUTORTFM_ENSURE_MSG_V(Condition, Format, Args) do { \
|
|
if (AUTORTFM_UNLIKELY(!(Condition))) { \
|
|
[[maybe_unused]] static bool bCalled = [&] { \
|
|
::AutoRTFM::EnsureFailureV(__FILE__, __LINE__, /* ProgramCounter */ nullptr, #Condition, Format, Args); \
|
|
return true; \
|
|
}(); \
|
|
} } while(false)
|
|
|
|
#define AUTORTFM_DUMP_STACKTRACE(Message) ::AutoRTFM::Log(Message, ##__VA_ARGS__)
|
|
|
|
// This is all a bit funky - but for good reason! We want `AUTORTFM_ASSERT`
|
|
// to be as close to *zero* cost if the assert wouldn't trigger.
|
|
// We will always pay the cost of the unlikely branch, but we need
|
|
// to make the body of taking the assert happen in another function
|
|
// so as not to affect codegen, register allocation, and stack
|
|
// reservation. But we also want the `AUTORTFM_ASSERT` to give us accurate
|
|
// `__FILE__` and `__LINE__` information for the line that it was
|
|
// triggered on. So what we do is have a lambda at the line that
|
|
// actually does the assert (but crucially gets the correct file
|
|
// and line numbers), but then pass this to another function
|
|
// `DoAssert` which has a special calling convention that makes
|
|
// the caller as optimal as possible (at the expense of the callee).
|
|
#define AUTORTFM_ASSERT(exp) \
|
|
if (AUTORTFM_UNLIKELY(!(exp))) \
|
|
{ \
|
|
auto Lambda = [] \
|
|
{ \
|
|
AUTORTFM_FATAL("AUTORTFM_ASSERT(%s) failure", #exp); \
|
|
}; \
|
|
AutoRTFM::DoAssert(Lambda); \
|
|
}
|
|
|
|
// Same funkiness as AUTORTFM_ASSERT, except that it doesn't cause a fatal error.
|
|
// TODO: Maybe upgrade the Info -> Warning?
|
|
#define AUTORTFM_EXPECT(exp) \
|
|
if (AUTORTFM_UNLIKELY(!(exp))) \
|
|
{ \
|
|
auto Lambda = [] \
|
|
{ \
|
|
::AutoRTFM::LogWithCallstack(autortfm_log_info, "AUTORTFM_EXPECT(%s) failure", #exp); \
|
|
}; \
|
|
AutoRTFM::DoExpect(Lambda); \
|
|
}
|
|
|
|
#if defined(__has_feature)
|
|
#if __has_feature(address_sanitizer)
|
|
#define AUTORTFM_NO_ASAN [[clang::no_sanitize("address")]]
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(AUTORTFM_NO_ASAN)
|
|
#define AUTORTFM_NO_ASAN
|
|
#endif
|
|
|
|
#if defined(__clang__)
|
|
#define AUTORTFM_MUST_TAIL [[clang::musttail]]
|
|
#endif
|
|
|
|
#if !defined(AUTORTFM_MUST_TAIL)
|
|
#define AUTORTFM_MUST_TAIL
|
|
#endif
|
|
|
|
#if !defined(CA_SUPPRESS)
|
|
#define CA_SUPPRESS( WarningNumber )
|
|
#endif
|
|
|
|
[[noreturn]] inline void AutoRTFM::InternalUnreachable() //-V1082 Silence PVS [[noreturn]] false positive warning
|
|
{
|
|
AUTORTFM_FATAL("Unreachable encountered!");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
#endif // (defined(__AUTORTFM) && __AUTORTFM)
|