/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Debug.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "Debug.hpp" #include #if defined(AURORA_PLATFORM_WIN32) #include "ExceptionWatcher.Win32.hpp" #endif namespace Aurora::Debug { static AuUInt32 gFenceOSError = -1; AuUInt32 GetOSErrorFence() { return gFenceOSError; } #if defined(AURORA_PLATFORM_WIN32) AuString GetOSErrorStringWin32(DWORD error) { AuString ret; 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 nullptr; } ret = err; LocalFree(err); return ret; } std::optional 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; } std::optional TryGetOrFetchOSError() { static std::optional lastErrorError; static std::optional lastErrorFence; std::optional 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 std::optional TryFetchOSError() { return {}; } std::optional TryGetOrFetchOSError() { return {}; } #endif static AuUInt32 gFenceCError = -1; AuUInt32 GetCErrorFence() { return gFenceCError; } std::optional TryFetchCError() { static int lastError = 0; auto errorNumber = errno; if (!errorNumber) { return {}; } if (lastError == errorNumber) { return errorNumber; } gFenceCError++; return lastError = errorNumber; } std::optional TryGetOrFetchCError() { static std::optional lastErrorError; static std::optional lastErrorFence; std::optional 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, FailureCategory category, const char *msg) { LastError error{ address, category, msg ? msg : "" }; AuUInt32 rng = GetFenceId(); Telemetry::InsertManualFence(rng); Telemetry::InsertMsgError(error); TryGetOrFetchCError(); TryGetOrFetchOSError(); Telemetry::InsertManualFence(rng); #if defined(DEBUG) || defined(INTERNAL) 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 } }