AuroraRuntime/Source/Debug/Debug.cpp

308 lines
7.3 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.Win32.hpp"
#endif
namespace Aurora::Debug
{
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++;
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<int> TryFetchCError()
{
static int lastError = 0;
auto errorNumber = errno;
if (!errorNumber)
{
return {};
}
if (lastError == errorNumber)
{
return errorNumber;
}
gFenceCError++;
return lastError = errorNumber;
}
AuOptional<int> TryGetOrFetchCError()
{
static AuOptional<int> lastErrorError;
static AuOptional<AuUInt32> lastErrorFence;
AuOptional<int> 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 GetFenceId()
{
return GetCErrorFence() << 8 ^ GetOSErrorFence(); // preserve lowest 8 bits, or in new additional bits, overlapping bits can get fucked
}
AUKN_SYM void PrintError()
{
AuUInt32 rng = GetFenceId();
Telemetry::InsertManualFence(rng);
static AuUInt32 cLastFence = 0;
auto cFence = GetCErrorFence();
auto cError = TryGetOrFetchCError();
if ((cError) && (cFence != cLastFence))
{
LogWarn("Language Error: {} ({})", strerror(*cError), *cError);
cLastFence = cFence;
}
static AuUInt32 osLastFence = 0;
auto osFence = GetOSErrorFence();
auto osError = TryGetOrFetchOSError();
if ((osError) && (osFence != osLastFence))
{
LogWarn("Operating System Error: {} (0x{:x})", osError->second, osError->first);
osLastFence = osFence;
}
Telemetry::InsertManualFence(rng);
}
void CheckErrors()
{
AuUInt32 rng = GetFenceId();
Telemetry::InsertManualFence(rng);
TryGetOrFetchCError();
TryGetOrFetchOSError();
Telemetry::InsertManualFence(rng);
}
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::InsertManualFence(rng);
Telemetry::InsertMsgError(error);
TryGetOrFetchCError();
TryGetOrFetchOSError();
Telemetry::InsertManualFence(rng);
// 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();
LogWarn("ERROR: {}", error.dbg);
#endif
}
AUKN_SYM AuString StackTraceEntry::Stringify() const
{
const auto frame = *this;
AuString backTraceBuffer;
backTraceBuffer.reserve(512 - 32); // 512 seems like a nice length minus some overhead for a bucket allocator
backTraceBuffer += fmt::format("\tAddress: {:x}", frame.address);
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;
}
AUKN_SYM AuString StringifyStackTrace(const StackTrace &backtrace)
{
AuString backTraceBuffer;
backTraceBuffer.reserve(2048);
backTraceBuffer += "Unwinding call frame:";
for (const auto &frame : backtrace)
{
backTraceBuffer += "\n";
backTraceBuffer += frame.Stringify();
}
return backTraceBuffer;
}
void InitDebug()
{
#if defined(AURORA_PLATFORM_WIN32)
InitWin32();
#endif
}
}