diff --git a/Code/base/threading/ThreadUtils.cpp b/Code/base/threading/ThreadUtils.cpp new file mode 100644 index 000000000..0530801fa --- /dev/null +++ b/Code/base/threading/ThreadUtils.cpp @@ -0,0 +1,46 @@ + +#include "ThreadUtils.h" + +namespace +{ +// as defined in sentry-native/src/sentry_sync.h +#ifdef _WIN32 +#include +using sentry_threadid_t = HANDLE; + +sentry_threadid_t GetCurrentThreadHandle() +{ + return ::GetCurrentThread(); +} +#else +#include +using sentry_threadid_t = pthread_t; + +sentry_threadid_t GetCurrentThreadHandle() +{ + return ::pthread_self(); +} +#endif + +} // namespace + +// we use the sentry impl here, as it covers all bases, e.g linux & windows support, plus additional windows 10+ +// features +extern "C" +{ + int sentry__thread_setname(sentry_threadid_t aThreadHandle, const char* apThreadName); +} + +namespace base +{ +bool SetThreadName(void* apThreadHandle, const char* apThreadName) +{ + return sentry__thread_setname(reinterpret_cast(apThreadHandle), apThreadName) == 0; +} + +bool SetCurrentThreadName(const char* apThreadName) +{ + return sentry__thread_setname(GetCurrentThreadHandle(), apThreadName) == 0; +} + +} // namespace base diff --git a/Code/base/threading/ThreadUtils.h b/Code/base/threading/ThreadUtils.h new file mode 100644 index 000000000..7b59fc10f --- /dev/null +++ b/Code/base/threading/ThreadUtils.h @@ -0,0 +1,10 @@ +#pragma once + +namespace base +{ +// set to thread handle +bool SetThreadName(void* apThreadHandle, const char* apThreadName); + +// call this on current thread +bool SetCurrentThreadName(const char* apThreadName); +} // namespace base diff --git a/Code/base/xmake.lua b/Code/base/xmake.lua index 239ccdd62..5d7e1fe4a 100644 --- a/Code/base/xmake.lua +++ b/Code/base/xmake.lua @@ -7,7 +7,8 @@ target("BaseLib") add_headerfiles("**.h") add_files("**.cpp") add_packages( - "tiltedcore", + "tiltedcore", + "sentry-native", "hopscotch-map", "gtest", "spdlog") diff --git a/Code/client/Games/Skyrim/BSSystem/BSTaskletManager.cpp b/Code/client/Games/Skyrim/BSSystem/BSTaskletManager.cpp new file mode 100644 index 000000000..9c4506212 --- /dev/null +++ b/Code/client/Games/Skyrim/BSSystem/BSTaskletManager.cpp @@ -0,0 +1,35 @@ + +#include + +namespace +{ +struct BSTaskletManager +{ + char pad0[0x30]; + void* threadHandles[6]; +}; + +static void (*Construct_TaskletManager)(BSTaskletManager*); + +static void Hook_Construct_TaskletManager(BSTaskletManager* apSelf) +{ + Construct_TaskletManager(apSelf); + + for (int i = 0; i < 6; i++) + { + if (!apSelf->threadHandles[i]) + continue; + + auto name = fmt::format("TaskletThread{}", i); + base::SetThreadName(apSelf->threadHandles[i], name.c_str()); + } +} +} // namespace + +static TiltedPhoques::Initializer s_BSThreadInit([]() { + const VersionDbPtr getTaskletManagerInstance(69554); + + // tasklet naming + TiltedPhoques::SwapCall(getTaskletManagerInstance.Get() + 0x63, Construct_TaskletManager, + &Hook_Construct_TaskletManager); +}); diff --git a/Code/client/Games/Skyrim/BSSystem/BSThread.cpp b/Code/client/Games/Skyrim/BSSystem/BSThread.cpp new file mode 100644 index 000000000..0fff2cabb --- /dev/null +++ b/Code/client/Games/Skyrim/BSSystem/BSThread.cpp @@ -0,0 +1,95 @@ + +#include +#include + +namespace +{ +void (*BSThread_Initialize)(BSThread*, int, const char*){nullptr}; + +void Hook_BSThread_Initialize(BSThread* apThis, int aStackSize, const char* apName) +{ + BSThread_Initialize(apThis, aStackSize, apName); + + if (!apName && apThis->m_ThreadHandle) + spdlog::warn("Unnamed thread started: {}", apThis->m_ThreadID); + + // this means the thread was successfully created + if (apThis->m_ThreadHandle) + { + bool result = base::SetThreadName(apThis->m_ThreadHandle, apName); + if (!result) + { + spdlog::warn("Failed to set thread name for tid {} to {}", apThis->m_ThreadID, apName); + } + } +} + +// bsthreadutils +// hook this in order to redirect the game to use the new naming apis (windows 10+) +void Hook_SetThreadName(uint32_t aThreadId, const char* apThreadName) +{ + // query thread handle + if (auto hThread = ::OpenThread(THREAD_QUERY_INFORMATION, FALSE, aThreadId)) + { + base::SetThreadName(hThread, apThreadName); + + ::CloseHandle(hThread); + } + else + spdlog::warn("Hook_SetThreadName(): Unable to query thread handle :("); +} + +// experimental stuff.. +#if 0 +typedef struct +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +void Hook_RaiseException(DWORD, DWORD, DWORD, const THREADNAME_INFO* lpArguments) +{ + base::SetCurrentThreadName(lpArguments->szName); +} + +HANDLE WINAPI Hook_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId) +{ + auto hThread = + CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); + + if (hThread && lpThreadId) + { + auto name = fmt::format("BNetThread{}", *lpThreadId); + base::SetThreadName(hThread, name.c_str()); + } + + return hThread; +} +#endif +} // namespace + +static TiltedPhoques::Initializer s_BSThreadInit([]() { + const VersionDbPtr threadInit(68261); + BSThread_Initialize = static_cast(threadInit.GetPtr()); + // need to detour this for now :/ + TP_HOOK_IMMEDIATE(&BSThread_Initialize, &Hook_BSThread_Initialize); + + const VersionDbPtr setThreadName(69066); + TiltedPhoques::Jump(setThreadName.Get(), &Hook_SetThreadName); + +#if 0 + const VersionDbPtr createHavokThread(57704); + // relatively safe to do, since this is unlikely to ever change, as beth wont update havok + TiltedPhoques::Nop(createHavokThread.Get() + 0x81, 6); + + TiltedPhoques::PutCall(createHavokThread.Get() + 0x81, &Hook_RaiseException); + + + TiltedPhoques::Nop(0x1411F0FD4, 6); + TiltedPhoques::PutCall(0x1411F0FD4, &Hook_CreateThread); +#endif +}); diff --git a/Code/client/Games/Skyrim/BSSystem/BSThread.h b/Code/client/Games/Skyrim/BSSystem/BSThread.h new file mode 100644 index 000000000..0549117e7 --- /dev/null +++ b/Code/client/Games/Skyrim/BSSystem/BSThread.h @@ -0,0 +1,20 @@ +#pragma once + +class BSThread +{ + public: + virtual ~BSThread() = 0; + // runner + virtual uint32_t ThreadProc() = 0; + // probably release + virtual bool Release() = 0; + + //private: + CRITICAL_SECTION lock; // in reality a BSCriticalSection + void* m_ThreadHandle; + void* m_ParentHandle; + uint32_t m_ThreadID; + uint32_t m_ParentID; + bool bThreadIsActive; +}; +static_assert(sizeof(BSThread) == 0x50, "BSThread size mismatch"); diff --git a/Code/client/Games/Skyrim/Projectiles/Projectile.cpp b/Code/client/Games/Skyrim/Projectiles/Projectile.cpp index b1c287576..2addb46c4 100644 --- a/Code/client/Games/Skyrim/Projectiles/Projectile.cpp +++ b/Code/client/Games/Skyrim/Projectiles/Projectile.cpp @@ -108,11 +108,11 @@ static TiltedPhoques::Initializer s_projectileHooks([]() { TP_HOOK(&RealLaunch, HookLaunch); - static VersionDbPtr hookLoc(34452); + VersionDbPtr hookLoc(34452); struct C : TiltedPhoques::CodeGenerator { - C() + C(uint8_t* apLoc) { // replicate mov(rbx, ptr[rsp + 0x50]); @@ -121,7 +121,7 @@ static TiltedPhoques::Initializer s_projectileHooks([]() { cmp(rbx, 0); jz("exit"); // jump back - jmp_S(uintptr_t(hookLoc.Get()) + 0x379); + jmp_S(apLoc + 0x379); L("exit"); // return false; scratch space from the registers @@ -137,7 +137,7 @@ static TiltedPhoques::Initializer s_projectileHooks([]() { pop(rbp); ret(); } - } gen; - TiltedPhoques::Jump(uintptr_t(hookLoc.Get()) + 0x374, gen.getCode()); + } gen(hookLoc.Get()); + TiltedPhoques::Jump(hookLoc.Get() + 0x374, gen.getCode()); }); diff --git a/Code/client/Games/Skyrim/RTTI.h b/Code/client/Games/Skyrim/RTTI.h index 4ca0ca4ba..6b371ea88 100644 --- a/Code/client/Games/Skyrim/RTTI.h +++ b/Code/client/Games/Skyrim/RTTI.h @@ -512,8 +512,8 @@ struct NiStream; extern template struct internal::RttiLocator; struct InterfacedClass; extern template struct internal::RttiLocator; -struct BSThread; -extern template struct internal::RttiLocator; +class BSThread; +extern template class internal::RttiLocator; struct TESBipedModelForm; extern template struct internal::RttiLocator; struct QueuedActor; diff --git a/Code/client/Services/Generic/DiscordService.cpp b/Code/client/Services/Generic/DiscordService.cpp index be4009878..0ac6acf12 100644 --- a/Code/client/Services/Generic/DiscordService.cpp +++ b/Code/client/Services/Generic/DiscordService.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #define DISCORD_OVERLAY_ENABLE 0 @@ -206,6 +207,8 @@ bool DiscordService::Init() //TODO (Force): i want to move this away from its own thread //this is done because discord needs to be ticked before world static std::thread updateThread([&]() { + base::SetCurrentThreadName("DiscordCallbacks"); + while (!m_bRequestThreadKillHack) { const auto runResult = m_pCore->run_callbacks(m_pCore); diff --git a/Code/client/xmake.lua b/Code/client/xmake.lua index 84656e67c..c214d0456 100644 --- a/Code/client/xmake.lua +++ b/Code/client/xmake.lua @@ -39,6 +39,7 @@ target(name) add_deps( "UiProcess", "CommonLib", + "BaseLib", "ImGuiImpl", "TiltedConnect", "TiltedReverse", diff --git a/Code/immersive_launcher/Main.cpp b/Code/immersive_launcher/Main.cpp index 4a2622b55..a200825f0 100644 --- a/Code/immersive_launcher/Main.cpp +++ b/Code/immersive_launcher/Main.cpp @@ -3,6 +3,7 @@ #include "launcher.h" #include #include +#include #include "script_extender/SEMemoryBlock.h" #include @@ -53,6 +54,8 @@ struct ComScope int main(int argc, char** argv) { + base::SetCurrentThreadName("MainLauncherThread"); + // memory block for Script Extender reserved as early as we can script_extender::SEMemoryBlock b; if (!b.Good()) diff --git a/Code/immersive_launcher/launcher.aps b/Code/immersive_launcher/launcher.aps index 7ec19e98a..2df2c971c 100644 Binary files a/Code/immersive_launcher/launcher.aps and b/Code/immersive_launcher/launcher.aps differ diff --git a/Code/server_runner/DediRunner.cpp b/Code/server_runner/DediRunner.cpp index 1bee10d5a..629220cf0 100644 --- a/Code/server_runner/DediRunner.cpp +++ b/Code/server_runner/DediRunner.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace { @@ -86,6 +87,8 @@ void DediRunner::RunGSThread() void DediRunner::StartTerminalIO() { m_pConIOThread.reset(new std::jthread([&]() { + base::SetCurrentThreadName("ConsoleIO"); + spdlog::get("ConOut")->info("Server console"); PrintExecutorArrowHack(); diff --git a/Code/server_runner/main.cpp b/Code/server_runner/main.cpp index b761cbe32..60bb7cf85 100644 --- a/Code/server_runner/main.cpp +++ b/Code/server_runner/main.cpp @@ -11,8 +11,10 @@ #include #include -#include +#include + #include "DediRunner.h" +#include namespace { @@ -150,6 +152,8 @@ int main(int argc, char** argv) if (!CheckBuildTag(kBuildTag)) return 1; + base::SetCurrentThreadName("ServerRunnerMain"); + LogInstance logger; (void)logger;