From 27098f8f704bb39addef979a46ee70047e814a49 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 19 Dec 2019 17:46:10 +0000 Subject: [PATCH] Give our threads debugger-friendly names --- qrenderdoc/Code/CaptureContext.cpp | 4 ++ .../Code/Interface/ShaderProcessingTool.cpp | 1 + qrenderdoc/Code/QRDUtils.cpp | 63 +++++++++++++++++++ qrenderdoc/Code/QRDUtils.h | 10 +++ qrenderdoc/Code/ReplayManager.cpp | 3 + qrenderdoc/Windows/BufferViewer.cpp | 1 + qrenderdoc/Windows/Dialogs/CaptureDialog.cpp | 2 + qrenderdoc/Windows/MainWindow.cpp | 4 ++ qrenderdoc/Windows/PythonShell.cpp | 1 + renderdoc/android/android.cpp | 4 ++ renderdoc/core/remote_server.cpp | 4 ++ renderdoc/core/replay_proxy.cpp | 2 + renderdoc/core/target_control.cpp | 4 ++ renderdoc/os/os_specific.h | 2 + .../os/posix/android/android_threading.cpp | 4 ++ renderdoc/os/posix/apple/apple_threading.cpp | 4 ++ renderdoc/os/posix/ggp/ggp_threading.cpp | 4 ++ renderdoc/os/posix/linux/linux_threading.cpp | 6 ++ renderdoc/os/win32/win32_process.cpp | 2 + renderdoc/os/win32/win32_threading.cpp | 49 +++++++++++++++ 20 files changed, 174 insertions(+) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 6d0100fee..9df08cba4 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -702,6 +702,7 @@ void CaptureContext::LoadCapture(const rdcstr &captureFile, const ReplayOptions new LambdaThread([this, captureFile, opts, origFilename, temporary, local]() { LoadCaptureThreaded(captureFile, opts, origFilename, temporary, local); }); + thread->setName(lit("LoadCapture")); thread->selfDelete(true); thread->start(); @@ -1095,6 +1096,7 @@ void CaptureContext::RecompressCapture() LambdaThread *th = new LambdaThread([cap, destFilename, &progress]() { cap->Convert(destFilename.toUtf8().data(), "rdc", NULL, [&progress](float p) { progress = p; }); }); + th->setName(lit("RecompressCapture")); th->start(); // wait a few ms before popping up a progress bar th->wait(500); @@ -1319,6 +1321,7 @@ bool CaptureContext::ImportCapture(const CaptureFileFormat &fmt, const rdcstr &i [&progress](float p) { progress = 0.5f + p * 0.5f; }); file->Shutdown(); }); + th->setName(lit("ImportCapture")); th->start(); // wait a few ms before popping up a progress bar th->wait(500); @@ -1394,6 +1397,7 @@ void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &e status = file->Convert(exportfile.c_str(), ext.toUtf8().data(), sdfile, [&progress](float p) { progress = p; }); }); + th->setName(lit("ExportCapture")); th->start(); // wait a few ms before popping up a progress bar th->wait(500); diff --git a/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp index ffb32e9c1..b21cbad33 100644 --- a/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp +++ b/qrenderdoc/Code/Interface/ShaderProcessingTool.cpp @@ -158,6 +158,7 @@ static ShaderToolOutput RunTool(const ShaderProcessingTool &tool, QWidget *windo process.moveToThread(mainThread); }); + thread->setName(lit("ShaderProcessingTool %s").arg(tool.name)); thread->moveObjectToThread(&process); thread->start(); diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 69d5300b6..c5ea1104a 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -2275,6 +2275,7 @@ void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinis GUIInvoke::call(&dialog, [&dialog]() { dialog.closeAndReset(); }); }); + progressTickerThread.setName(lit("Progress Dialog")); progressTickerThread.start(); // show the dialog @@ -2448,3 +2449,65 @@ void *AccessWaylandPlatformInterface(const QByteArray &resource, QWindow *window QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); return native->nativeResourceForWindow(resource, window); } + +// Default Qt doesn't do this in release Qt builds, which is all we use +#if defined(Q_OS_WIN32) + +#include + +typedef HRESULT(WINAPI *PFN_SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); + +const DWORD MS_VC_EXCEPTION = 0x406D1388; +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO +{ + 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; +#pragma pack(pop) + +static void SetThreadNameWithException(const char *name) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)(&info)); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} + +void LambdaThread::windowsSetName() +{ + // try to use the fancy modern API + static PFN_SetThreadDescription setThreadDesc = (PFN_SetThreadDescription)GetProcAddress( + GetModuleHandleA("kernel32.dll"), "SetThreadDescription"); + + if(setThreadDesc) + { + setThreadDesc(GetCurrentThread(), m_Name.toStdWString().c_str()); + } + else + { + // don't throw the exception if there's no debugger present + if(!IsDebuggerPresent()) + return; + + SetThreadNameWithException(m_Name.toStdString().c_str()); + } +} + +#else + +void LambdaThread::windowsSetName() +{ +} + +#endif diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 2d53a2cdf..a885a2f28 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -318,10 +318,15 @@ private: QThread *m_Thread; QSemaphore completed; bool m_SelfDelete = false; + QString m_Name; + + void windowsSetName(); public slots: void process() { + if(!m_Name.isEmpty()) + windowsSetName(); m_func(); m_Thread->quit(); m_Thread = NULL; @@ -342,6 +347,11 @@ public: QObject::connect(m_Thread, &QThread::finished, m_Thread, &QThread::deleteLater); } + void setName(QString name) + { + m_Name = name; + m_Thread->setObjectName(name); + } void start(QThread::Priority prio = QThread::InheritPriority) { m_Thread->start(prio); } bool isRunning() { return completed.available(); } bool wait(unsigned long time = ULONG_MAX) diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index 78e7b2fc5..58674c1d9 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -54,6 +54,7 @@ void ReplayManager::OpenCapture(const QString &capturefile, const ReplayOptions m_Thread = new LambdaThread([this, proxyRenderer, capturefile, opts, progress]() { run(proxyRenderer, capturefile, opts, progress); }); + m_Thread->setName(lit("ReplayManager")); m_Thread->start(QThread::HighestPriority); while(m_Thread->isRunning() && !m_Running) @@ -185,6 +186,7 @@ rdcstr ReplayManager::CopyCaptureToRemote(const rdcstr &localpath, QWidget *wind { LambdaThread *thread = new LambdaThread([&lambda]() { lambda(NULL); }); thread->selfDelete(true); + thread->setName(lit("CopyCaptureToRemote")); thread->start(); } @@ -219,6 +221,7 @@ void ReplayManager::CopyCaptureFromRemote(const rdcstr &remotepath, const rdcstr { LambdaThread *thread = new LambdaThread([&lambda]() { lambda(NULL); }); thread->selfDelete(true); + thread->setName(lit("CopyCaptureFromRemote")); thread->start(); } diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index bb48c06df..e79ac237f 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -2560,6 +2560,7 @@ void BufferViewer::populateBBox(PopulateBufferData *bufdata) GUIInvoke::call(this, [this, bbox]() { UI_UpdateBoundingBox(*bbox); }); }); + thread->setName(lit("BBox calc")); thread->selfDelete(true); thread->start(); diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index 23fca81a0..28a710791 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -585,6 +585,7 @@ void CaptureDialog::CheckAndroidSetup(QString &filename) } }); + scan->setName(lit("CheckAndroidSetup")); scan->start(); scan->deleteLater(); } @@ -663,6 +664,7 @@ Would you like RenderDoc to try patching your package? } }); + patch->setName(lit("Android patch")); patch->start(); // wait a few ms before popping up a progress bar patch->wait(500); diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 3ea32b9c2..a69273f35 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -181,6 +181,7 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai } } }); + m_RemoteProbe->setName(lit("Remote Probe")); m_RemoteProbe->start(); SetTitle(); @@ -607,6 +608,7 @@ void MainWindow::OnCaptureTrigger(const QString &exe, const QString &workingDir, callback(live); }); }); + th->setName(lit("ExecuteAndInject")); th->start(); // wait a few ms before popping up a progress bar th->wait(500); @@ -1943,6 +1945,7 @@ void MainWindow::setRemoteHost(int hostIdx) // update status host.CheckStatus(); }); + launchthread->setName(lit("Remote host launch")); launchthread->start(); ShowProgressDialog(this, tr("Attempting to update remote server, please wait..."), @@ -2017,6 +2020,7 @@ void MainWindow::setRemoteHost(int hostIdx) m_Ctx.GetCaptureDialog()->UpdateRemoteHost(); }); }); + th->setName(lit("Remote host check")); th->selfDelete(true); th->start(); } diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 1e385fd85..cc1348fa1 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -713,6 +713,7 @@ void PythonShell::on_runScript_clicked() }); }); + thread->setName(lit("Python script")); thread->selfDelete(true); thread->start(); } diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index a35cfc433..263e7ccdb 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -696,6 +696,8 @@ struct AndroidController : public IDeviceProtocolHandler void ThreadEntry() { + Threading::SetCurrentThreadName("AndroidController"); + while(Atomic::CmpExch32(&running, 1, 1) == 1) { Threading::Sleep(5); @@ -956,6 +958,8 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w // we spin up a thread to Ping() every second, since starting a package can block for a long time. volatile int32_t done = 0; Threading::ThreadHandle pingThread = Threading::CreateThread([&done, this]() { + Threading::SetCurrentThreadName("Android Ping"); + bool ok = true; while(ok && Atomic::CmpExch32(&done, 0, 0) == 0) ok = Ping(); diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 682e51dfb..88269a80b 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -154,6 +154,8 @@ struct ClientThread static void InactiveRemoteClientThread(ClientThread *threadData) { + Threading::SetCurrentThreadName("InactiveRemoteClientThread"); + uint32_t ip = threadData->socket->GetRemoteIP(); { @@ -209,6 +211,8 @@ static void InactiveRemoteClientThread(ClientThread *threadData) static void ActiveRemoteClientThread(ClientThread *threadData, RENDERDOC_PreviewWindowCallback previewWindow) { + Threading::SetCurrentThreadName("ActiveRemoteClientThread"); + Network::Socket *&client = threadData->socket; #if ENABLED(RDOC_DEVEL) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index e2cc11555..a13ad7dd2 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -2570,6 +2570,8 @@ void ReplayProxy::EndRemoteExecution() void ReplayProxy::RemoteExecutionThreadEntry() { + Threading::SetCurrentThreadName("RemoteExecutionThreadEntry"); + // while we're alive while(Atomic::CmpExch32(&m_RemoteExecutionKill, 0, 0) == 0) { diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp index 74e7b2dcf..bfa6627a8 100644 --- a/renderdoc/core/target_control.cpp +++ b/renderdoc/core/target_control.cpp @@ -100,6 +100,8 @@ rdcstr DoStringise(const PacketType &el) void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *client) { + Threading::SetCurrentThreadName("TargetControlClientThread"); + Threading::KeepModuleAlive(); WriteSerialiser writer(new StreamWriter(client, Ownership::Nothing), Ownership::Stream); @@ -375,6 +377,8 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli void RenderDoc::TargetControlServerThread(Network::Socket *sock) { + Threading::SetCurrentThreadName("TargetControlServerThread"); + Threading::KeepModuleAlive(); RenderDoc::Inst().m_SingleClientName = ""; diff --git a/renderdoc/os/os_specific.h b/renderdoc/os/os_specific.h index 5eaa117d6..f189b7366 100644 --- a/renderdoc/os/os_specific.h +++ b/renderdoc/os/os_specific.h @@ -145,6 +145,8 @@ void SetTLSValue(uint64_t slot, void *value); // must typedef CriticalSectionTemplate CriticalSection +void SetCurrentThreadName(const rdcstr &name); + typedef uint64_t ThreadHandle; ThreadHandle CreateThread(std::function entryFunc); uint64_t GetCurrentID(); diff --git a/renderdoc/os/posix/android/android_threading.cpp b/renderdoc/os/posix/android/android_threading.cpp index 03ebf249e..735199856 100644 --- a/renderdoc/os/posix/android/android_threading.cpp +++ b/renderdoc/os/posix/android/android_threading.cpp @@ -38,3 +38,7 @@ uint64_t Timing::GetTick() clock_gettime(CLOCK_MONOTONIC, &ts); return uint64_t(ts.tv_sec) * 1000000000ULL + uint32_t(ts.tv_nsec & 0xffffffff); } + +void Threading::SetCurrentThreadName(const rdcstr &name) +{ +} diff --git a/renderdoc/os/posix/apple/apple_threading.cpp b/renderdoc/os/posix/apple/apple_threading.cpp index 29af186e0..0f91face8 100644 --- a/renderdoc/os/posix/apple/apple_threading.cpp +++ b/renderdoc/os/posix/apple/apple_threading.cpp @@ -40,3 +40,7 @@ uint64_t Timing::GetTick() { return mach_absolute_time(); } + +void Threading::SetCurrentThreadName(const rdcstr &name) +{ +} diff --git a/renderdoc/os/posix/ggp/ggp_threading.cpp b/renderdoc/os/posix/ggp/ggp_threading.cpp index c9cf07234..071470d21 100644 --- a/renderdoc/os/posix/ggp/ggp_threading.cpp +++ b/renderdoc/os/posix/ggp/ggp_threading.cpp @@ -38,3 +38,7 @@ uint64_t Timing::GetTick() clock_gettime(CLOCK_MONOTONIC, &ts); return uint64_t(ts.tv_sec) * 1000000000ULL + uint32_t(ts.tv_nsec & 0xffffffff); } + +void Threading::SetCurrentThreadName(const rdcstr &name) +{ +} diff --git a/renderdoc/os/posix/linux/linux_threading.cpp b/renderdoc/os/posix/linux/linux_threading.cpp index 03ebf249e..c7f7745c6 100644 --- a/renderdoc/os/posix/linux/linux_threading.cpp +++ b/renderdoc/os/posix/linux/linux_threading.cpp @@ -24,6 +24,7 @@ #include "os/os_specific.h" +#include #include #include @@ -38,3 +39,8 @@ uint64_t Timing::GetTick() clock_gettime(CLOCK_MONOTONIC, &ts); return uint64_t(ts.tv_sec) * 1000000000ULL + uint32_t(ts.tv_nsec & 0xffffffff); } + +void Threading::SetCurrentThreadName(const rdcstr &name) +{ + prctl(PR_SET_NAME, (unsigned long)name.c_str(), 0, 0, 0); +} diff --git a/renderdoc/os/win32/win32_process.cpp b/renderdoc/os/win32/win32_process.cpp index c80a4ecac..985c714f1 100644 --- a/renderdoc/os/win32/win32_process.cpp +++ b/renderdoc/os/win32/win32_process.cpp @@ -1346,6 +1346,8 @@ static GlobalHookData *globalHook = NULL; // the global hook. static void GlobalHookThread() { + Threading::SetCurrentThreadName("GlobalHookThread"); + // keep looping doing an atomic compare-exchange to check that finished is still 0 while(Atomic::CmpExch32(&globalHook->finished, 0, 0) == 0) { diff --git a/renderdoc/os/win32/win32_threading.cpp b/renderdoc/os/win32/win32_threading.cpp index af6213f7f..c26e39b58 100644 --- a/renderdoc/os/win32/win32_threading.cpp +++ b/renderdoc/os/win32/win32_threading.cpp @@ -262,6 +262,55 @@ ThreadHandle CreateThread(std::function entryFunc) return (ThreadHandle)h; } +typedef HRESULT(WINAPI *PFN_SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); + +const DWORD MS_VC_EXCEPTION = 0x406D1388; +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO +{ + 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; +#pragma pack(pop) + +static void SetThreadNameWithException(const char *name) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)(&info)); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} + +void SetCurrentThreadName(const rdcstr &name) +{ + // try to use the fancy modern API + static PFN_SetThreadDescription setThreadDesc = (PFN_SetThreadDescription)GetProcAddress( + GetModuleHandleA("kernel32.dll"), "SetThreadDescription"); + + if(setThreadDesc) + { + setThreadDesc(GetCurrentThread(), StringFormat::UTF82Wide(name).c_str()); + } + else + { + // don't throw the exception if there's no debugger present + if(!IsDebuggerPresent()) + return; + + SetThreadNameWithException(name.c_str()); + } +} + uint64_t GetCurrentID() { return (uint64_t)::GetCurrentThreadId();