Reece
d8e000b5c3
[*] Fix up gen1 copypasta [+] ConsoleMessage::ToPersistentString [*] Adjust ConsoleMessage formatting
402 lines
9.6 KiB
C++
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
|
|
}
|
|
} |