AuroraRuntime/Source/Debug/Debug.cpp
Reece d8e000b5c3 [*] Prevent WriteLater from entering a deadlock condition. OnFlushs are no longer atomic
[*] Fix up gen1 copypasta
[+] ConsoleMessage::ToPersistentString
[*] Adjust ConsoleMessage formatting
2022-01-27 20:28:20 +00:00

402 lines
9.6 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>
#if defined(AURORA_PLATFORM_WIN32)
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#endif
namespace Aurora::Debug
{
static thread_local AuUInt32 tlsLastBackTrace = 0xFFFFFFFF;
static thread_local StackTrace tlsLastStackTrace;
static thread_local AuString tlsLastExceptionMessage;
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 {};
}
ret = err;
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()
{
return {};
}
AUKN_SYM AuString GetLastException()
{
AU_LOCK_GUARD(gLock);
return tlsLastExceptionMessage;
}
AUKN_SYM OSError_t GetLastSystemMessage()
{
return TryGetOrFetchOSError().value_or(OSError_t{});
}
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();
}
AUKN_SYM void _PushError(AuUInt address, EFailureCategory category, const char *msg)
{
LastError error {address, category, msg ? msg : ""};
// Oi, developer
#if defined(DEBUG)
DebugBreak();
#endif
// 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();
// Is anyone listening?
// Print to console if internal
// Eh, dont spam nested to a logger if its not going to make sense to a normie
if ((category == EFailureCategory::kFailureNested) && (msg == nullptr))
{
return;
}
#if defined(DEBUG) || defined(STAGING)
PrintError();
AuLogWarn("ERROR: {}", error.dbg);
#endif
}
AUKN_SYM AuString StackTraceEntry::Stringify() const
{
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", std::get<0>(re), std::get<1>(re), std::get<2>(re));
}
else
{
backTraceBuffer += "\t\t[proprietary]\n";
}
return backTraceBuffer;
}
catch (...)
{
return {};
}
}
AUKN_SYM AuString StringifyStackTrace(const StackTrace &backtrace)
{
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()
{
#if defined(AURORA_PLATFORM_WIN32)
InitWin32();
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
InitNT();
#endif
}
}