[+] AuBitsToLower, AuBitsToHigher (returns half of an input word)

[+] AuPopCnt
[+] NormalizePath, GetFileFromPath, GetDirectoryFromPath, GoUpToSeparator
[*] Fix Version Helpers again
[*] Fix registry locale memory check under read registry in SWInfo
[*] Breakout portable process code away from evil win32 code
[*] .dynlib -> .dylib under macos
This commit is contained in:
Reece Wilson 2022-01-27 05:24:08 +00:00
parent fbd437d3d4
commit d7c6d66fad
27 changed files with 985 additions and 298 deletions

View File

@ -38,6 +38,10 @@ namespace Aurora::IO::FS
AUKN_SYM bool Copy(const AuString &src, const AuString &dest);
AUKN_SYM bool NormalizePath(AuString &out, const AuString &in);
AUKN_SYM bool GetFileFromPath(AuString &out, const AuString &path);
AUKN_SYM bool GetDirectoryFromPath(AuString &out, const AuString &path);
AUKN_SYM bool GoUpToSeparator(AuString &out, const AuString &path);
}
#include "IFileStream.hpp"

View File

@ -14,7 +14,6 @@ namespace Aurora::Telemetry
eSpecsMetadata,
eMessage,
eStackWarning,
eWinCxxException,
eAccessViolation,
eAssertion,
ePanic,
@ -37,6 +36,7 @@ namespace Aurora::Telemetry
struct NewBlockBoxEntryStackReport
{
AuString str;
Debug::StackTrace backtrace;
AuUInt32 fenceId;
};
@ -70,13 +70,6 @@ namespace Aurora::Telemetry
NewBlockBoxEntryStackReport stack;
};
struct NewBlockBoxEntryWin32CxxException
{
NewBlockBoxEntryStackReport stack;
AuString str;
AuUInt32 fenceId;
};
struct NewBlockboxEntryBasicMessage
{
Console::ConsoleMessage message;
@ -154,7 +147,6 @@ namespace Aurora::Telemetry
NewBlockboxEntryMessage message;
NewBlockboxEntryMessage assertion;
NewBlockBoxEntryStackReport stack;
NewBlockBoxEntryWin32CxxException wincxx;
NewBlockboxEntryBasicMessage log;
NewBlockboxEntryBasicMessage panic;
NewBlockBoxEntryMemoryViolation violation;

View File

@ -737,7 +737,110 @@ static auline bool AuBitScanForward(AuUInt8 &index, T value)
return success;
}
// TODO: AuPopCnt
template<typename T>
struct AuHalfWord
{
using ReturnType_t = AuConditional_t<AuIsSame_v<T, AuUInt64>, AuUInt32, AuConditional_t<AuIsSame_v<T, AuUInt32>, AuUInt32, AuConditional_t<AuIsSame_v<T, AuUInt16>, AuUInt8, AuFalseType>>>;
static ReturnType_t ToLower(T in)
{
if constexpr (AuIsSame_v<T, AuUInt64>)
{
return in & AuUInt64(0xFFFFFFFF);
}
else if constexpr (AuIsSame_v<T, AuUInt32>)
{
return in & 0xFFFF;
}
else if constexpr (AuIsSame_v<T, AuUInt16>)
{
return in & 0xFF;
}
else
{
return {};
}
}
static ReturnType_t ToHigher(T in)
{
if constexpr (AuIsSame_v<T, AuUInt64>)
{
return (in >> AuUInt64(32)) & AuUInt64(0xFFFFFFFF);
}
else if constexpr (AuIsSame_v<T, AuUInt32>)
{
return (in >> 16) & 0xFFFF;
}
else if constexpr (AuIsSame_v<T, AuUInt16>)
{
return (in >> 8) & 0xFF;
}
else
{
return {};
}
}
};
template<typename T>
static auto AuBitsToLower(T in)
{
return AuHalfWord<T>::ToLower(in);
}
template<typename T>
static auto AuBitsToHigher(T in)
{
return AuHalfWord<T>::ToHigher(in);
}
template<typename T>
static AuUInt8 AuPopCnt(T in)
{
#if defined(AURORA_COMPILER_MSVC)
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86)
if constexpr (sizeof(T) == sizeof(AuUInt64))
return _mm_popcnt_u64(in);
else if constexpr (sizeof(T) < sizeof(AuUInt32))
return __popcnt(in);
else if constexpr (sizeof(T) <= sizeof(AuUInt16))
return __popcnt16(in);
#endif
#else
if constexpr (sizeof(T) == sizeof(unsigned long long))
return __builtin_popcountll(static_cast<unsigned long long>(value));
else if constexpr (sizeof(T) == sizeof(unsigned long))
return __builtin_popcountl(static_cast<unsigned long>(value));
else if constexpr (sizeof(T) == sizeof(unsigned int))
return __builtin_popcount(static_cast<unsigned int>(value));
#endif
#if defined(AU_CPU_ENDIAN_LITTLE)
if constexpr (sizeof(T) == sizeof(AuUInt64))
{
AuUInt64 m1 = 0x5555555555555555ll;
AuUInt64 m2 = 0x3333333333333333ll;
AuUInt64 m4 = 0x0F0F0F0F0F0F0F0Fll;
AuUInt64 h01 = 0x0101010101010101ll;
in -= (in >> 1) & m1;
in = (in & m2) + ((in >> 2) & m2);
in = (in + (in >> 4)) & m4;
return (in * h01) >> 56;
}
else if constexpr (sizeof(T) == sizeof(AuUInt32))
{
in = in - ((in >> 1) & 0x55555555);
in = (in & 0x33333333) + ((in >> 2) & 0x33333333);
return (((in + (in >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
}
#endif
return {};
}
// TODO: AuBitScanReverse
#if defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_ARM)

View File

@ -10,6 +10,7 @@
#include <Source/Telemetry/Telemetry.hpp>
#if defined(AURORA_PLATFORM_WIN32)
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#endif
@ -394,5 +395,9 @@ namespace Aurora::Debug
#if defined(AURORA_PLATFORM_WIN32)
InitWin32();
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
InitNT();
#endif
}
}

View File

@ -31,6 +31,6 @@ namespace Aurora::Debug
AuOptional<AuUInt32> TryFetchCError();
void CheckErrors();
void PlatformHandleFatal(bool fatal);
void InitDebug();
}

View File

@ -0,0 +1,244 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ExceptionWatcher.NT.cpp
Date: 2022-1-26
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "ExceptionWatcher.NT.hpp"
#include <ehdata.h>
#if defined(AURORA_PLATFORM_WIN32)
#include "ExceptionWatcher.Win32.hpp"
#include "Stack.Win32.hpp"
#endif
#include "Debug.hpp"
#include <Source/Telemetry/Telemetry.hpp>
namespace Aurora::Debug
{
static std::string kStringRawName = typeid(std::string).raw_name();
static bool IsReadable(const void *address)
{
MEMORY_BASIC_INFORMATION info;
if (!VirtualQuery(address, &info, sizeof(info)))
{
return false;
}
if (!info.BaseAddress)
{
return false;
}
return (info.Protect & (PAGE_READONLY | PAGE_READWRITE)) != 0;
}
AuString ReportSEH(HMODULE handle, void *exception, const void *throwInfo, const AuFunction<AuString()> &resolveFallback, const StackTrace &trace, const AuFunction<void(const AuString &)> &prereport)
{
AuString message;
if (handle && handle != INVALID_HANDLE_VALUE)
{
try
{
const ThrowInfo *pthrowInfo = reinterpret_cast<const ThrowInfo *>(throwInfo);
auto attribs = pthrowInfo->attributes;
const auto catchableTypeArray = reinterpret_cast<const CatchableTypeArray *>(reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(pthrowInfo->pCatchableTypeArray));
AuString suffix;
for (int i = 0; i < catchableTypeArray->nCatchableTypes; i++)
{
const auto type = reinterpret_cast<CatchableType *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(catchableTypeArray->arrayOfCatchableTypes[i]));
const auto descriptor = reinterpret_cast<std::type_info *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(type->pType));
message += (i == 0 ? "" : AuString(", ")) + descriptor->name(); // __std_type_info_name
if (_strnicmp(descriptor->raw_name(), ".?AVException@", 15 - 1) == 0)
{
auto exception2 = reinterpret_cast<std::exception *>(exception);
auto wptr = exception2->what();
if (wptr)
{
suffix = wptr;
}
}
else if (_strnicmp(descriptor->raw_name(), ".PEAD", 5) == 0)
{
auto possibleStringPointer = reinterpret_cast<const char **>(exception);
if (IsReadable(possibleStringPointer))
{
auto string = *possibleStringPointer;
if (IsReadable(string) && (strnlen(string, 4096) < 1024))
{
suffix = string;
}
}
}
else if (_strnicmp(descriptor->raw_name(), kStringRawName.data(), kStringRawName.size()) == 0)
{
auto possibleStdStringPointer = reinterpret_cast<std::string *>(exception);
if (IsReadable(possibleStdStringPointer))
{
suffix = *possibleStdStringPointer;
}
}
}
if (suffix.size())
{
message += AuString("\r\n ") + suffix;
}
}
catch (...)
{
}
}
if (message.find("invalid sto") != AuString::npos) return message;
try
{
if (message.empty() && resolveFallback)
{
message = resolveFallback();
}
}
catch (...)
{
}
if (prereport)
{
prereport(message);
}
try
{
Telemetry::NewBlockboxEntry entry;
entry.type = Telemetry::ENewBlackBoxEntry::eStackWarning;
entry.stack.fenceId = ReportStackTrace(trace, message);
entry.stack.backtrace = trace;
Telemetry::Report(entry);
}
catch (...)
{
}
return message;
}
static void HandleFatal(bool fatal, _EXCEPTION_POINTERS *pExceptionInfo)
{
static bool handlingFatal = false;
if (std::exchange(handlingFatal, true))
{
std::terminate();
}
#if defined(DEBUG) || defined(INTERNAL)
if (!fatal)
{
if (IsDebuggerPresent()) __debugbreak();
else BlackboxReport(pExceptionInfo);
}
else
{
SaveMinidump(pExceptionInfo);
}
#else
BlackboxReport(pExceptionInfo);
#endif
}
void PlatformHandleFatal(bool fatal)
{
_EXCEPTION_POINTERS ptrs;
StackTrace ret;
CONTEXT ctx;
if (!GetThreadContext(GetCurrentThread(), &ctx))
{
Debug::Panic();
}
ptrs.ExceptionRecord = {};
ptrs.ContextRecord = &ctx;
HandleFatal(fatal, &ptrs);
}
void InitNT()
{
SetUnhandledExceptionFilter([](_EXCEPTION_POINTERS *pExceptionInfo) -> LONG
{
AuLogWarn("Handling fatal NT err exception...");
HandleFatal(true, pExceptionInfo);
return EXCEPTION_EXECUTE_HANDLER;
});
}
}
static thread_local AuUInt tlsThrowCounter;
extern "C" AUKN_SYM void __stdcall _ReportMSVCSEH(void *exception, const void *throwInfo, void *caller)
{
if (!throwInfo) return;
if (!exception) return;
HMODULE handle = 0;
if (_EH_RELATIVE_TYPEINFO)
{
if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCSTR>(caller), reinterpret_cast<HMODULE *>(&handle)))
{
return;
}
}
if (!handle)
{
return;
}
if (handle == INVALID_HANDLE_VALUE)
{
return;
}
// we will probably overflow for a bit if an inner try catch borks up somewhere
if ((tlsThrowCounter++) == 7)
{
tlsThrowCounter--;
return;
}
try
{
auto trace = AuDebug::GetStackTrace();
AuDebug::ReportSEH(handle, exception, throwInfo, {}, trace,
[&](const AuString &str)
{
AuLogWarn("Local MSVC Exception: 0x{:x}, {}", exception, str.c_str());
AuLogWarn("{}", StringifyStackTrace(trace));
});
}
catch (...)
{
}
tlsThrowCounter--;
}

View File

@ -0,0 +1,17 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ExceptionWatcher.NT.hpp
Date: 2022-1-26
Author: Reece
***/
#pragma once
namespace Aurora::Debug
{
AuString ReportSEH(HMODULE handle, void *exception, const void *throwInfo, const AuFunction<AuString()> &resolveFallback, const StackTrace &trace, const AuFunction<void(const AuString &)> &prereport);
void InitNT();
}
extern "C" AUKN_SYM void __stdcall _ReportMSVCSEH(void *exception, const void *throwInfo, void *caller);

View File

@ -5,3 +5,6 @@
Date: 2021-6-12
Author: Reece
***/
#pragma once

View File

@ -7,7 +7,9 @@
***/
#include <Source/RuntimeInternal.hpp>
#include "Debug.hpp"
#include "ExceptionWatcher.NT.hpp"
#include "ExceptionWatcher.Win32.hpp"
#include "Stack.Win32.hpp"
#include <Source/Process/ProcessMap.Win32.hpp>
#include <Source/Telemetry/Telemetry.hpp>
@ -20,116 +22,12 @@
#include <ehdata.h>
#include <Source/Process/ProcessMap.hpp>
#include <Source/IO/FS/FS.hpp>
static thread_local int gDebugLocked = 0;
namespace Aurora::Debug
{
static AuUInt GetImageBase(HMODULE mod)
{
if (!mod)
{
return 0;
}
if (mod == INVALID_HANDLE_VALUE)
{
return 0;
}
auto cache = Process::GetFromModuleCache(reinterpret_cast<AuUInt>(mod));
if (!cache.moduleMeta)
{
return 0;
}
return cache.moduleMeta->origVa;
}
static void ParseStack(CONTEXT *ctx, StackTrace &backTrace)
{
char buffer[sizeof(SYMBOL_INFO) + (MAX_SYM_NAME + 1) * sizeof(char)] = { 0 };
AuString backTraceBuffer;
HMODULE hModule;
DWORD64 displacement = 0;
HANDLE process = GetCurrentProcess();
STACKFRAME64 stack = { 0 };
CONTEXT cpy = *ctx;
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
#if defined(AURORA_ARCH_X64)
stack.AddrPC.Offset = ctx->Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = ctx->Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = ctx->Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
#elif defined(AURORA_ARCH_XX86)
stack.AddrPC.Offset = ctx->Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = ctx->Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = ctx->Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
#endif
for (ULONG frame = 0; ; frame++)
{
StackTraceEntry frameCurrent;
auto result = StackWalk64
(
#if defined(AURORA_ARCH_X64)
IMAGE_FILE_MACHINE_AMD64,
#else
IMAGE_FILE_MACHINE_I386,
#endif
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
&stack,
&cpy,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
if (!result)
{
break;
}
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
frameCurrent.address = stack.AddrPC.Offset;
frameCurrent.relAddress = frameCurrent.address;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(stack.AddrPC.Offset), &hModule))
{
if (hModule != NULL)
{
frameCurrent.module = Process::ModuleToPath(hModule);
frameCurrent.relAddress = frameCurrent.relAddress - reinterpret_cast<AuUInt>(hModule) + GetImageBase(hModule);
}
}
#if defined(DEBUG) || defined(STAGING)
IMAGEHLP_LINE64 line;
DWORD disp;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, &line))
{
frameCurrent.file = AuMakeTuple(line.FileName, line.LineNumber, 0);
}
#endif
backTrace.push_back(frameCurrent);
}
}
#define EXCEPTION_ENTRY(n) {n, #n}
static const AuHashMap<DWORD, AuString> kExceptionTable
{
@ -202,174 +100,268 @@ namespace Aurora::Debug
return (info.Protect & (PAGE_READONLY | PAGE_READWRITE)) != 0;
}
static LONG HandleVectorException(_EXCEPTION_POINTERS *ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode < STATUS_GUARD_PAGE_VIOLATION)
{
return EXCEPTION_CONTINUE_SEARCH;
}
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
{
return EXCEPTION_CONTINUE_SEARCH;
}
auto minimal = gDebugLocked++;
if (minimal >= 5)
{
SysPanic("Nested Exception");
}
bool cxxThrow = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_MAGIC_NUMBER1;
bool cxxThrowPure = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_PURE_MAGIC_NUMBER1;
void *exception {};
HMODULE handle {};
ThrowInfo *pThrowInfo {};
if ((ExceptionInfo->ExceptionRecord->ExceptionCode == EH_EXCEPTION_NUMBER) &&
(ExceptionInfo->ExceptionRecord->NumberParameters >= 4) &&
((cxxThrow) ||
(cxxThrowPure))
)
{
pThrowInfo = reinterpret_cast<ThrowInfo *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[2]);
if (pThrowInfo)
{
auto attribs = pThrowInfo->attributes;
if (_EH_RELATIVE_TYPEINFO)
{
handle = reinterpret_cast<HMODULE>(ExceptionInfo->ExceptionRecord->ExceptionInformation[3]);
}
exception = reinterpret_cast<void *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
}
}
bool isCritical = AuTryFind(kExceptionFatalTable, ExceptionInfo->ExceptionRecord->ExceptionCode);
auto handleNoCppObject = [&]() -> AuString
{
const AuString *msg;
if (AuTryFind(kExceptionTable, ExceptionInfo->ExceptionRecord->ExceptionCode, msg))
{
return *msg;
}
else
{
return AuToString(ExceptionInfo->ExceptionRecord->ExceptionCode);
}
};
StackTrace backtrace;
ParseStack(ExceptionInfo->ContextRecord, backtrace);
#if defined(STAGING) || defined(DEBUG)
bool isInternal = true;
#else
bool isInternal = false;
#endif
ReportSEH(handle, exception, pThrowInfo, handleNoCppObject, backtrace, [&](const AuString &str)
{
// Pre-submit callback -> its showtime
if ((isCritical || isInternal) && (minimal == 0))
{
AuLogWarn("NT Exception: 0x{:x}, {}", ExceptionInfo->ExceptionRecord->ExceptionCode, str);
AuLogWarn("{}", StringifyStackTrace(backtrace));
}
});
try
{
if (isCritical)
{
Telemetry::Mayday();
}
if (isCritical)
{
PlatformHandleFatal(true);
}
}
catch (...)
{
}
gDebugLocked = 0;
return EXCEPTION_CONTINUE_SEARCH;
}
static AuString GetDumpName()
{
AuString exeName;
try
{
Process::GetProcName(exeName);
}
catch (...)
{
}
try
{
auto tm = Time::ToCivilTime(Time::CurrentClockMS());
return fmt::format("{}-{}-{}T{}-{}-{}Z_{}.dmp", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
exeName);
}
catch (...)
{
return "errordate.dmp";
}
}
#if defined(DEBUG) || defined(INTERNAL)
void SaveMinidump(_EXCEPTION_POINTERS *ExceptionInfo)
{
bool ok {};
MINIDUMP_EXCEPTION_INFORMATION info;
info.ThreadId = GetCurrentThreadId();
info.ClientPointers = ExceptionInfo != nullptr;
info.ExceptionPointers = ExceptionInfo;
static const DWORD flags = MiniDumpWithFullMemory |
MiniDumpWithFullMemoryInfo |
MiniDumpWithHandleData |
MiniDumpWithUnloadedModules |
MiniDumpWithThreadInfo;
std::wstring path;
while (path.empty())
{
try
{
path = Locale::ConvertFromUTF8(AuIOFS::NormalizePathRet("./Logs/Crashes/" + GetDumpName()));
}
catch (...)
{
}
}
auto hFile = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
{
AuLogWarn("Couldn't open minidump file. Has a debugger locked the .dmp file?");
goto miniDumpOut;
}
ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)flags, &info, nullptr, nullptr);
if (!ok)
{
AuLogWarn("Couldn't write minidump");
goto miniDumpOut;
}
CloseHandle(hFile);
miniDumpOut:
try
{
AuDebug::PrintError();
}
catch (...)
{
}
__debugbreak();
__fastfail('fokd');
}
#endif
void BlackboxReport(_EXCEPTION_POINTERS *ExceptionInfo)
{
AuString path;
HANDLE hFile;
std::wstring wpath;
MINIDUMP_EXCEPTION_INFORMATION info;
auto ok = AuIOFS::GetProfileDomain(path);
if (!ok)
{
AuLogWarn("Couldn't find minidump directory");
goto miniMiniDumpOut;
}
info.ClientPointers = true;
info.ThreadId = GetCurrentThreadId();
info.ExceptionPointers = ExceptionInfo;
static const DWORD flags = MiniDumpWithDataSegs |
MiniDumpWithFullMemoryInfo |
MiniDumpWithHandleData |
MiniDumpWithUnloadedModules |
MiniDumpWithThreadInfo;
while (wpath.empty())
{
try
{
wpath = Locale::ConvertFromUTF8(path + "Crashes/" + GetDumpName());
}
catch (...)
{
}
}
hFile = CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
AuLogWarn("[1] Couldn't open minidump file. Has a debugger locked the .dmp file?");
goto miniMiniDumpOut;
}
ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)flags, &info, nullptr, nullptr);
if (!ok)
{
AuLogWarn("Couldn't write minidump");
goto miniMiniDumpOut;
}
CloseHandle(hFile);
miniMiniDumpOut:
Telemetry::NewBlackBoxEntryMinidump report {};
report.includesRx = false;
report.resource.path = path;
report.resource.type = Telemetry::ENewBlackBoxResourceType::eLocal;
Telemetry::ReportDyingBreath(report);
__fastfail('fokd');
}
void InitWin32()
{
static AuString kStringRawName = typeid(AuString).raw_name();
#if defined(DEBUG) || defined(STAGING)
SymInitialize(GetCurrentProcess(), NULL, TRUE);
#endif
// This is really gross :(
AddVectoredExceptionHandler(1,
[](_EXCEPTION_POINTERS *ExceptionInfo) -> LONG
{
Telemetry::NewBlockboxEntry entry;
entry.type = Telemetry::ENewBlackBoxEntry::eWinCxxException;
if (ExceptionInfo->ExceptionRecord->ExceptionCode < STATUS_GUARD_PAGE_VIOLATION)
{
return EXCEPTION_CONTINUE_SEARCH;
}
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
{
return EXCEPTION_CONTINUE_SEARCH;
}
auto minimal = gDebugLocked++;
if (minimal >= 5)
{
SysPanic("Nested Exception");
}
bool cxxThrow = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_MAGIC_NUMBER1;
bool cxxThrowPure = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_PURE_MAGIC_NUMBER1;
if ((ExceptionInfo->ExceptionRecord->ExceptionCode == EH_EXCEPTION_NUMBER) &&
(ExceptionInfo->ExceptionRecord->NumberParameters >= 4) &&
((cxxThrow) ||
(cxxThrowPure))
)
{
HMODULE handle {};
auto *throwInfo = reinterpret_cast<ThrowInfo *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[2]);
if (throwInfo)
{
auto attribs = throwInfo->attributes;
if (_EH_RELATIVE_TYPEINFO)
{
handle = reinterpret_cast<HMODULE>(ExceptionInfo->ExceptionRecord->ExceptionInformation[3]);
}
const auto catchableTypeArray = reinterpret_cast<const CatchableTypeArray*>(reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(throwInfo->pCatchableTypeArray));
AuString suffix;
for (int i = 0; i < catchableTypeArray->nCatchableTypes; i++)
{
const auto type = reinterpret_cast<CatchableType *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(catchableTypeArray->arrayOfCatchableTypes[i]));
const auto descriptor = reinterpret_cast<std::type_info *> (reinterpret_cast<AuUInt>(handle) + static_cast<AuUInt>(type->pType));
entry.wincxx.str += (i == 0 ? "" : AuString(", ")) + descriptor->name(); // __std_type_info_name
if (strnicmp(descriptor->raw_name(), ".?AVException@", AuArraySize(".?AVException@") - 1) == 0)
{
auto exception = reinterpret_cast<std::exception *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
auto wptr = exception->what();
if (wptr)
{
suffix = wptr;
}
}
else if (strncmp(descriptor->raw_name(), ".PEAD", AuArraySize(".PEAD")) == 0)
{
auto possibleStringPointer = reinterpret_cast<const char **>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
if (IsReadable(possibleStringPointer))
{
auto string = *possibleStringPointer;
if (IsReadable(string) && (strnlen(string, 4096) < 1024))
{
suffix = string;
}
}
}
else if (strncmp(descriptor->raw_name(), kStringRawName.data(), kStringRawName.size()) == 0)
{
auto possibleStdStringPointer = reinterpret_cast<AuString *>(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
if (IsReadable(possibleStdStringPointer))
{
auto string = *possibleStdStringPointer;
suffix = string;
}
}
}
if (suffix.size())
{
entry.wincxx.str += AuString("\r\n ") + suffix;
}
}
}
bool isCritical = AuTryFind(kExceptionFatalTable, ExceptionInfo->ExceptionRecord->ExceptionCode);
if (entry.wincxx.str.empty())
{
const AuString *msg;
if (AuTryFind(kExceptionTable, ExceptionInfo->ExceptionRecord->ExceptionCode, msg))
{
entry.wincxx.str = *msg;
}
else
{
entry.wincxx.str = AuToString(ExceptionInfo->ExceptionRecord->ExceptionCode);
}
}
else
{
if (entry.wincxx.str.find("invalid sto") != std::string::npos)
{
gDebugLocked = 0;
return EXCEPTION_CONTINUE_SEARCH;
}
}
try
{
if (minimal < 2)
{
ParseStack(ExceptionInfo->ContextRecord, entry.wincxx.stack.backtrace);
}
}
catch (...)
{
}
entry.wincxx.fenceId = ReportStackTrace(entry.wincxx.stack.backtrace, entry.wincxx.str);
#if defined(STAGING) || defined(DEBUG)
bool isInternal = true;
#else
bool isInternal = false;
#endif
if ((isCritical || isInternal) && (minimal == 0))
{
AuLogWarn("NT Exception: 0x{:x}, {}", ExceptionInfo->ExceptionRecord->ExceptionCode, entry.wincxx.str);
AuLogWarn("{}", StringifyStackTrace(entry.wincxx.stack.backtrace));
}
try
{
Telemetry::Report(entry);
if (isCritical)
{
Telemetry::Mayday();
}
}
catch (...)
{
}
gDebugLocked = 0;
return EXCEPTION_CONTINUE_SEARCH;
});
AddVectoredExceptionHandler(1, HandleVectorException);
}
}

View File

@ -10,4 +10,9 @@
namespace Aurora::Debug
{
void InitWin32();
#if defined(DEBUG) || defined(INTERNAL)
void SaveMinidump(_EXCEPTION_POINTERS *ExceptionInfo);
#endif
void BlackboxReport(_EXCEPTION_POINTERS *ExceptionInfo);
}

View File

@ -13,6 +13,8 @@
namespace Aurora::Debug
{
void PlatformHandleFatal();
AUKN_SYM void DebugBreak()
{
#if defined(DEBUG) || defined(STAGING)

View File

@ -0,0 +1,8 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Android.cpp
Date: 2022-1-26
Author: Reece
***/

View File

@ -0,0 +1,9 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Android.hpp
Date: 2022-1-26
Author: Reece
***/
#pragma once

View File

@ -0,0 +1,7 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Unix.cpp
Date: 2022-1-26
Author: Reece
***/

View File

@ -0,0 +1,9 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Unix.hpp
Date: 2022-1-26
Author: Reece
***/
#pragma once

View File

@ -0,0 +1,131 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Win32.cpp
Date: 2022-1-26
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Stack.hpp"
#include "Stack.Win32.hpp"
#include <Dbghelp.h>
#include <Source/Process/ProcessMap.hpp>
#include <Source/Process/ProcessMap.Win32.hpp>
namespace Aurora::Debug
{
static AuUInt GetImageBase(HMODULE mod)
{
if (!mod)
{
return 0;
}
if (mod == INVALID_HANDLE_VALUE)
{
return 0;
}
auto cache = Process::GetFromModuleCache(reinterpret_cast<AuUInt>(mod));
if (!cache.moduleMeta)
{
return 0;
}
return cache.moduleMeta->origVa;
}
void ParseStack(CONTEXT *ctx, StackTrace &backTrace)
{
char buffer[sizeof(SYMBOL_INFO) + (MAX_SYM_NAME + 1) * sizeof(char)] = { 0 };
AuString backTraceBuffer;
HMODULE hModule;
DWORD64 displacement = 0;
HANDLE process = GetCurrentProcess();
STACKFRAME64 stack = { 0 };
CONTEXT cpy = *ctx;
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
#if defined(AURORA_ARCH_X64)
stack.AddrPC.Offset = ctx->Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = ctx->Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = ctx->Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
#elif defined(AURORA_ARCH_X86)
stack.AddrPC.Offset = ctx->Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = ctx->Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = ctx->Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
#endif
for (ULONG frame = 0; ; frame++)
{
StackTraceEntry frameCurrent;
auto result = StackWalk64
(
#if defined(AURORA_ARCH_X64)
IMAGE_FILE_MACHINE_AMD64,
#else
IMAGE_FILE_MACHINE_I386,
#endif
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
&stack,
&cpy,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
if (!result)
{
break;
}
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
frameCurrent.address = stack.AddrPC.Offset;
frameCurrent.relAddress = frameCurrent.address;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(stack.AddrPC.Offset), &hModule))
{
if (hModule != NULL)
{
frameCurrent.module = Process::ModuleToPath(hModule);
frameCurrent.relAddress = frameCurrent.relAddress - reinterpret_cast<AuUInt>(hModule) + GetImageBase(hModule);
}
}
#if defined(DEBUG) || defined(STAGING)
IMAGEHLP_LINE64 line;
DWORD disp;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, &line))
{
frameCurrent.file = AuMakeTuple(line.FileName, line.LineNumber, 0);
}
#endif
backTrace.push_back(frameCurrent);
}
}
StackTrace PlatformWalkCallStack()
{
StackTrace ret;
CONTEXT ctx;
if (!GetThreadContext(GetCurrentThread(), &ctx)) return {};
ParseStack(&ctx, ret);
return ret;
}
}

View File

@ -0,0 +1,15 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.Win32.hpp
Date: 2022-1-26
Author: Reece
***/
#pragma once
namespace Aurora::Debug
{
void ParseStack(CONTEXT *ctx, StackTrace &backTrace);
StackTrace PlatformWalkCallStack();
}

9
Source/Debug/Stack.hpp Normal file
View File

@ -0,0 +1,9 @@
/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Stack.hpp
Date: 2022-1-26
Author: Reece
***/
#pragma once

View File

@ -242,4 +242,128 @@ namespace Aurora::IO::FS
return ok;
}
AUKN_SYM bool NormalizePath(AuString &out, const AuString &in)
{
try
{
out = NormalizePathRet(in);
return out.size();
}
catch (...)
{
return false;
}
}
static AuUInt GetLastSplitterIndex(const AuString &path)
{
AuUInt indexA {}, indexB {};
auto a = path.find_last_of('\\');
if (a != AuString::npos) indexA = a;
auto b = path.find_last_of('/');
if (b != AuString::npos) indexB = b;
return AuMax(a, b);
}
AUKN_SYM bool GetFileFromPath(AuString &out, const AuString &path)
{
try
{
if (path.empty()) return false;
if (path[path.size() - 1] == '.') return false;
AuUInt max = GetLastSplitterIndex(path);
if (max == path.size()) return false;
out = path.substr(max + 1);
return true;
}
catch (...)
{
return false;
}
}
AUKN_SYM bool GetDirectoryFromPath(AuString &out, const AuString &path)
{
try
{
if (path.empty()) return false;
if (path[path.size() - 1] == '.')
{
if (path.size() > 2 && (path[path.size() - 2] == '\\' || path[path.size() - 2] == '/'))
{
out = path.substr(0, path.size() - 1);
}
else
{
return false;
}
}
AuUInt max = GetLastSplitterIndex(path);
if (!max)
{
return false;
}
if (max == path.size())
{
out = path;
return true;
}
out = path.substr(0, max + 1);
return true;
}
catch (...)
{
out.clear();
return false;
}
}
AUKN_SYM bool GoUpToSeparator(AuString &out, const AuString &path)
{
try
{
if (path.empty()) return false;
if (path[path.size() - 1] == '.')
{
if (path.size() > 2 && (path[path.size() - 2] == '\\' || path[path.size() - 2] == '/'))
{
out = path.substr(0, path.size() - 2);
}
else
{
return false;
}
}
AuUInt max = GetLastSplitterIndex(path);
if (!max)
{
return false;
}
if (max == path.size())
{
out = path.substr(0, max - 1);
return true;
}
out = path.substr(0, max);
return true;
}
catch (...)
{
out.clear();
return false;
}
}
}

View File

@ -62,7 +62,7 @@ namespace Aurora::Process
return ".so";
case Build::EPlatform::ePlatformAppleMacOS:
case Build::EPlatform::ePlatformIos:
return ".dynlib";
return ".dylib";
default:
return nullptr;
}

View File

@ -69,7 +69,7 @@ namespace Aurora::SWInfo
}
strValue = Locale::ConvertFromWChar(in.data(), in.size());
return strValue.size() == in.size();
return strValue.size();
}
#endif

View File

@ -23,74 +23,74 @@ namespace Aurora::SWInfo
static AuUInt16 kWinVerWinBlue = 0x0603;
static AuUInt16 kWinVerWin10 = 0x0A00;
static bool IsWindowsVersionOrGreater(AuUInt16 major, AuUInt16 ignored, AuUInt16 sp)
static bool IsWindowsVersionOrGreater(AuUInt8 major, AuUInt8 minor, AuUInt16 sp)
{
auto &info = GetPlatformInfo();
return info.ePlatform == Build::EPlatform::ePlatformWin32 && info.uKernelMajor >= major && info.uUserlandMinor >= sp;
return info.ePlatform == Build::EPlatform::ePlatformWin32 && info.uKernelMajor >= major && info.uKernelMinor >= minor && info.uUserlandMinor >= sp;
}
AUKN_SYM bool IsWindowsXPOrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWinXP, kWinVerWinXP, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWinXP), AuBitsToLower(kWinVerWinXP), 0);
}
AUKN_SYM bool IsWindowsXPSP1OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWinXP, kWinVerWinXP, 1);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWinXP), AuBitsToLower(kWinVerWinXP), 1);
}
AUKN_SYM bool IsWindowsXPSP2OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWinXP, kWinVerWinXP, 2);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWinXP), AuBitsToLower(kWinVerWinXP), 2);
}
AUKN_SYM bool IsWindowsXPSP3OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWinXP, kWinVerWinXP, 3);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWinXP), AuBitsToLower(kWinVerWinXP), 3);
}
AUKN_SYM bool IsWindowsVistaOrGreater()
{
return IsWindowsVersionOrGreater(kWinVerVista, kWinVerVista, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerVista), AuBitsToLower(kWinVerVista), 0);
}
AUKN_SYM bool IsWindowsVistaSP1OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerVista, kWinVerVista, 1);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerVista), AuBitsToLower(kWinVerVista), 1);
}
AUKN_SYM bool IsWindowsVistaSP2OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerVista, kWinVerVista, 2);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerVista), AuBitsToLower(kWinVerVista), 2);
}
AUKN_SYM bool IsWindows7OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWin7, kWinVerWin7, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWin7), AuBitsToLower(kWinVerWin7), 0);
}
AUKN_SYM bool IsWindows7SP1OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWin7, kWinVerWin7, 1);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWin7), AuBitsToLower(kWinVerWin7), 1);
}
AUKN_SYM bool IsWindows8OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWin8, kWinVerWin8, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWin8), AuBitsToLower(kWinVerWin8), 0);
}
AUKN_SYM bool IsWindows8Point1OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWinBlue, kWinVerWinBlue, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWinBlue), AuBitsToLower(kWinVerWinBlue), 0);
}
AUKN_SYM bool IsWindows10OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWin10, kWinVerWin10, 0);
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWin10), AuBitsToLower(kWinVerWin10), 0);
}
AUKN_SYM bool IsWindows11OrGreater()
{
return IsWindowsVersionOrGreater(kWinVerWin10, kWinVerWin10, 0) && GetPlatformInfo().uKernelPatch >= 22000;
return IsWindowsVersionOrGreater(AuBitsToHigher(kWinVerWin10), AuBitsToLower(kWinVerWin10), 0) && GetPlatformInfo().uKernelPatch >= 22000;
}
}

View File

@ -9,6 +9,7 @@
#include <Source/Debug/Debug.hpp>
#include "Telemetry.hpp"
namespace Aurora::Telemetry
{
static AuThreadPrimitives::CriticalSectionUnique_t gGroupLock;
@ -53,6 +54,11 @@ namespace Aurora::Telemetry
}
void ReportDyingBreath(Telemetry::NewBlackBoxEntryMinidump &entry)
{
}
void ReportSysInfo()
{
@ -64,8 +70,11 @@ namespace Aurora::Telemetry
ReportSysInfo();
}
AUKN_SYM void Mayday()
{
Debug::CheckErrors();
Debug::PlatformHandleFatal(false);
}
}

View File

@ -18,5 +18,6 @@ namespace Aurora::Telemetry
void InsertBackTrace(AuUInt32 fence);
void ReportSysInfo();
void Report(Telemetry::NewBlockboxEntry &entry);
void ReportDyingBreath(Telemetry::NewBlackBoxEntryMinidump &entry);
void Init();
}

View File

@ -53,7 +53,6 @@ namespace Aurora::Threading::Primitives
{
return false;
}
}
if (atomicRelease_)

View File

@ -7,14 +7,13 @@
***/
#pragma once
namespace Aurora::Threading::Threads
{
AuPair<bool, AuUInt> /*success, oshandle*/ SpawnThread(const AuFunction<void()> &entrypoint, const AuString &debugString, AuUInt32 staskSize = 0);
}
#if defined(AURORA_IS_MODERNNT_DERIVED)
#include "SpawnThread.NT.hpp"
#include "SpawnThread.NT.hpp"
#elif defined(AURORA_IS_UNIX_DERIVED)
#include "SpawnThread.Unix.hpp"
#include "SpawnThread.Unix.hpp"
#endif