AuroraRuntime/Source/Debug/Debug.cpp
Jamie Reece Wilson 0f12603390 [+] AuAsync::NewWorkFunction
[+] AuAsync::IThreadPool::NewWorkFunction
[+] AU_DEBUG_MEMCRUNCH
2023-08-10 03:36:19 +01:00

468 lines
11 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Debug.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Debug.hpp"
#include <Source/Telemetry/Telemetry.hpp>
#include "MemoryCrunch.hpp"
#if defined(AURORA_PLATFORM_WIN32)
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#include "Stack.Win32.hpp"
#endif
#if defined(AURORA_IS_LINUX_DERIVED)
#include "Stack.Unix.hpp"
#endif
#include "ErrorStack.hpp"
namespace Aurora::Debug
{
static thread_local AuUInt32 tlsLastBackTrace = 0xFFFFFFFF;
static thread_local StackTrace tlsLastStackTrace;
static thread_local AuString tlsLastExceptionMessage;
static thread_local EFailureCategory tlsCategory = EFailureCategory::kFailureNone;
static AuUInt32 gStackTraceFence;
static AuUInt32 gFenceId;
static AuThreadPrimitives::SpinLock gLock;
static AuUInt32 gFenceOSError = -1;
AuUInt32 GetOSErrorFence()
{
return gFenceOSError;
}
#if defined(AURORA_IS_MODERNNT_DERIVED)
AuString GetOSErrorStringWin32(DWORD error)
{
AuString ret;
#if defined(AURORA_PLATFORM_WIN32)
char *err = nullptr;
if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&err,
0,
NULL))
{
return {};
}
try
{
ret = err;
}
catch (...)
{}
LocalFree(err);
#endif
return ret;
}
AuOptional<OSError_t> TryFetchOSError()
{
static OSError_t lastError{};
OSError_t ret{};
ret.first = GetLastError();
if ((ret.first == ERROR_SUCCESS) ||
(ret.first == WSAEWOULDBLOCK))
{
return {};
}
if (lastError.first == ret.first)
{
return lastError;
}
ret.second = GetOSErrorStringWin32(lastError.first);
gFenceOSError++;
gFenceId++;
return lastError = ret;
}
AuOptional<OSError_t> TryGetOrFetchOSError()
{
static AuOptional<OSError_t> lastErrorError;
static AuOptional<AuUInt32> lastErrorFence;
AuOptional<OSError_t> tempError = TryFetchOSError();
if (!tempError)
{
return lastErrorError;
}
if (lastErrorFence)
{
if (lastErrorFence.value() != GetOSErrorFence())
{
SetLastError(ERROR_SUCCESS);
Telemetry::InsertOSError(tempError.value());
}
}
lastErrorFence = GetOSErrorFence();
lastErrorError = tempError;
return tempError;
}
#else
AuOptional<OSError_t> TryFetchOSError()
{
return {};
}
AuOptional<OSError_t> TryGetOrFetchOSError()
{
return {};
}
#endif
static AuUInt32 gFenceCError = -1;
AuUInt32 GetCErrorFence()
{
return gFenceCError;
}
AuOptional<AuUInt32> TryFetchCError()
{
static AuUInt32 lastError = 0;
auto errorNumber = errno;
if (!errorNumber)
{
return {};
}
if (lastError == errorNumber)
{
return errorNumber;
}
gFenceCError++;
gFenceId++;
return lastError = errorNumber;
}
AuOptional<AuUInt32> TryGetOrFetchCError()
{
static AuOptional<AuUInt32> lastErrorError;
static AuOptional<AuUInt32> lastErrorFence;
AuOptional<AuUInt32> tempError = TryFetchCError();
if (!tempError)
{
return lastErrorError;
}
if (lastErrorFence)
{
if (lastErrorFence.value() != GetCErrorFence())
{
errno = 0;
Telemetry::InsertCError(tempError.value());
}
}
lastErrorFence = GetCErrorFence();
lastErrorError = tempError;
return tempError;
}
AuUInt32 ReportStackTrace(const StackTrace& trace, const AuString& message)
{
AU_LOCK_GUARD(gLock);
tlsLastStackTrace = trace;
tlsLastExceptionMessage = message;
tlsLastBackTrace = gStackTraceFence++;
gFenceId++;
return tlsLastBackTrace;
}
AuUInt32 GetFenceId()
{
return gFenceId++;
}
AUKN_SYM AuString GetLastErrorStack()
{
return StringifyStackTrace(GetLastStackTrace());
}
AUKN_SYM StackTrace GetLastStackTrace()
{
AU_LOCK_GUARD(gLock);
return tlsLastStackTrace;
}
AUKN_SYM StackTrace GetStackTrace()
{
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_IS_LINUX_DERIVED)
return PlatformWalkCallStack();
#else
return {};
#endif
}
AUKN_SYM AuString GetLastException()
{
AU_LOCK_GUARD(gLock);
return tlsLastExceptionMessage;
}
AUKN_SYM OSError_t GetLastSystemMessage()
{
return TryGetOrFetchOSError().value_or(OSError_t{});
}
AUKN_SYM void ErrorCategorySet(EFailureCategory category)
{
tlsCategory = category;
}
AUKN_SYM void ErrorCategoryClear()
{
tlsCategory = EFailureCategory::kFailureNone;
}
AUKN_SYM EFailureCategory ErrorCategoryGet()
{
return tlsCategory;
}
AUKN_SYM void PrintError()
{
AuUInt32 rng = GetFenceId();
Telemetry::BeginBlock();
try
{
Telemetry::InsertManualFence(rng);
static AuUInt32 cLastFence = 0;
auto cFence = GetCErrorFence();
auto cError = TryGetOrFetchCError();
if ((cError) && (cFence != cLastFence))
{
AuLogWarn("Language Error: {} ({})", strerror(*cError), *cError);
cLastFence = cFence;
}
static AuUInt32 osLastFence = 0;
auto osFence = GetOSErrorFence();
auto osError = TryGetOrFetchOSError();
if ((osError) && (osFence != osLastFence))
{
AuLogWarn("Operating System Error: {} (0x{:x})", osError->second, osError->first);
osLastFence = osFence;
}
Telemetry::InsertBackTrace(tlsLastBackTrace);
Telemetry::InsertManualFence(rng);
}
catch (...)
{
}
Telemetry::EndBlock();
}
AUKN_SYM void CheckErrors()
{
AuUInt32 rng = GetFenceId();
Telemetry::BeginBlock();
try
{
Telemetry::InsertManualFence(rng);
TryGetOrFetchCError();
TryGetOrFetchOSError();
Telemetry::InsertBackTrace(tlsLastBackTrace);
Telemetry::InsertManualFence(rng);
}
catch (...)
{
}
Telemetry::EndBlock();
}
#if 0
// compile me somewhere without public headers for compat if i care for some reason later (i probably wont)
AUKN_SYM void _PushError(AuUInt address, EFailureCategory category, const char *msg, AuUInt16 uLine);
AUKN_SYM void _PushError(AuUInt address, EFailureCategory category, const char *msg)
{
_PushError(address, category, msg, 0);
}
#endif
AUKN_SYM void _PushError(AuUInt address, EFailureCategory category, const char *msg, AuUInt16 uLine)
{
LastError error {address, category, msg};
// Oi, developer
#if defined(AU_CFG_ID_DEBUG)
DebugBreak();
#endif
tlsCategory = category;
try
{
if (ShouldPushErrorStackInternal())
{
auto pMessage = AuMakeSharedThrow<ThreadMessage>();
pMessage->uDebugBuildSourceLineHint = uLine;
pMessage->pStringMessage = AuMakeSharedThrow<AuString>(msg);
pMessage->eFailureCategory = category;
PushErrorStackInternal(pMessage);
}
}
catch (...)
{
}
// Cry about it to telemetry with other errors if available
AuUInt32 rng = GetFenceId();
Telemetry::BeginBlock();
try
{
Telemetry::InsertManualFence(rng);
Telemetry::InsertMsgError(error);
TryGetOrFetchCError();
TryGetOrFetchOSError();
Telemetry::InsertBackTrace(tlsLastBackTrace);
Telemetry::InsertManualFence(rng);
}
catch (...)
{
}
Telemetry::EndBlock();
// Eh, dont spam nested to a logger if its not going to make sense to a normie
if ((category == EFailureCategory::kFailureNested) && (msg == nullptr))
{
return;
}
// Print to console if internal
#if defined(DEBUG) || defined(STAGING)
PrintError();
// Is anyone listening?
AuLogWarn("ERROR: {}", error.pDbgMessage);
#endif
}
AUKN_SYM AuString StackTraceEntry::Stringify() const
{
AU_DEBUG_MEMCRUNCH;
const auto frame = *this;
AuString backTraceBuffer;
try
{
backTraceBuffer.reserve(512 - 32); // 512 seems like a nice length minus some overhead for a bucket allocator
backTraceBuffer += fmt::format("\tAddress: 0x{:x} (0x{:x})", frame.relAddress ? frame.relAddress : frame.address, frame.relAddress ? frame.address : frame.relAddress);
if (frame.module)
{
auto modName = frame.module.value();
if (modName.size())
{
backTraceBuffer += fmt::format(" within {}", modName);
}
else
{
backTraceBuffer += ", invalid module";
}
}
else
{
backTraceBuffer += ", unknown module";
}
if (frame.label)
{
backTraceBuffer += fmt::format(" ({}) \n", frame.label.value());
}
else
{
backTraceBuffer += ", unknown function\n";
}
if (frame.file)
{
const auto &re = frame.file.value();
backTraceBuffer += fmt::format("\t\t{}:{} ({}) \n", AuGet<0>(re), AuGet<1>(re), AuGet<2>(re));
}
else
{
backTraceBuffer += "\t\t[proprietary]\n";
}
return backTraceBuffer;
}
catch (...)
{
return {};
}
}
AUKN_SYM AuString StringifyStackTrace(const StackTrace &backtrace)
{
AU_DEBUG_MEMCRUNCH;
AuString backTraceBuffer;
try
{
backTraceBuffer.reserve(2048);
backTraceBuffer += "Unwinding call frame:";
for (const auto &frame : backtrace)
{
backTraceBuffer += "\n";
backTraceBuffer += frame.Stringify();
}
return backTraceBuffer;
}
catch (...)
{
return {};
}
}
void InitDebug()
{
InitMemoryCrunch();
#if defined(AURORA_PLATFORM_WIN32)
InitWin32();
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
InitNT();
#endif
}
}