From 1efab3f76307e96b5b4130cd9f0307c017dacb3d Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 2 Sep 2026 18:10:14 -0400 Subject: [PATCH] Host SOS in standalone LLDB driver Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/SOS/Strike/clrma/managedanalysis.cpp | 6 ++ src/SOS/lldbplugin/CMakeLists.txt | 8 ++ src/SOS/lldbplugin/driver.cpp | 102 +++++++++++++++++++++++ src/SOS/lldbplugin/services.cpp | 3 + src/SOS/lldbplugin/services.h | 25 +++++- src/SOS/lldbplugin/soscommand.cpp | 3 +- 6 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/SOS/lldbplugin/driver.cpp diff --git a/src/SOS/Strike/clrma/managedanalysis.cpp b/src/SOS/Strike/clrma/managedanalysis.cpp index 9e0b86ffc0..36ec2184e7 100644 --- a/src/SOS/Strike/clrma/managedanalysis.cpp +++ b/src/SOS/Strike/clrma/managedanalysis.cpp @@ -413,6 +413,12 @@ ClrmaManagedAnalysis::GetThread( #endif } + if (osThreadId == 0 || osThreadId == (ULONG)-1) + { + TraceError("GetThread resolved an invalid OS thread ID %08x\n", osThreadId); + return E_INVALIDARG; + } + if (m_clrmaService != nullptr) { if (FAILED(hr = m_clrmaService->GetThread(osThreadId, ppClrThread))) diff --git a/src/SOS/lldbplugin/CMakeLists.txt b/src/SOS/lldbplugin/CMakeLists.txt index 5ed1f71759..ab146785ac 100644 --- a/src/SOS/lldbplugin/CMakeLists.txt +++ b/src/SOS/lldbplugin/CMakeLists.txt @@ -135,5 +135,13 @@ add_library_clr(sosplugin SHARED ${SOURCES}) target_link_libraries(sosplugin ${LIBRARIES}) +if(CLR_CMAKE_HOST_OSX) + add_executable_clr(sos-lldb driver.cpp) + target_link_libraries(sos-lldb ${LLDB_LIB}) +endif() + # add the install targets install_clr(TARGETS sosplugin DESTINATIONS .) +if(CLR_CMAKE_HOST_OSX) + install_clr(TARGETS sos-lldb DESTINATIONS .) +endif() diff --git a/src/SOS/lldbplugin/driver.cpp b/src/SOS/lldbplugin/driver.cpp new file mode 100644 index 0000000000..d9f805f990 --- /dev/null +++ b/src/SOS/lldbplugin/driver.cpp @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include +#include +#include +#include +#include + +namespace +{ + void PrintUsage(const char* program) + { + std::fprintf(stderr, "Usage: %s [--no-lldbinit] [--batch] [-o command]...\n", program); + } + + bool ExecuteCommand(lldb::SBCommandInterpreter& interpreter, const std::string& command) + { + lldb::SBCommandReturnObject result; + result.SetImmediateOutputFile(stdout, false); + result.SetImmediateErrorFile(stderr, false); + interpreter.HandleCommand(command.c_str(), result, false); + std::fflush(stdout); + std::fflush(stderr); + + return result.GetStatus() != lldb::eReturnStatusQuit; + } +} + +int main(int argc, char** argv) +{ + bool batch = false; + bool sourceInitFiles = true; + std::vector startupCommands; + + for (int index = 1; index < argc; index++) + { + if (std::strcmp(argv[index], "--no-lldbinit") == 0) + { + sourceInitFiles = false; + } + else if (std::strcmp(argv[index], "--batch") == 0) + { + batch = true; + } + else if (std::strcmp(argv[index], "-o") == 0) + { + if (++index == argc) + { + std::fprintf(stderr, "Missing command after -o.\n"); + PrintUsage(argv[0]); + return 2; + } + startupCommands.emplace_back(argv[index]); + } + else + { + std::fprintf(stderr, "Unsupported argument: %s\n", argv[index]); + PrintUsage(argv[0]); + return 2; + } + } + + lldb::SBDebugger::Initialize(); + lldb::SBDebugger debugger = lldb::SBDebugger::Create(sourceInitFiles); + if (!debugger.IsValid()) + { + std::fprintf(stderr, "Failed to initialize LLDB.\n"); + lldb::SBDebugger::Terminate(); + return 1; + } + + debugger.SetAsync(false); + debugger.SetInputFileHandle(stdin, false); + debugger.SetOutputFileHandle(stdout, false); + debugger.SetErrorFileHandle(stderr, false); + + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + bool keepRunning = true; + for (const std::string& command : startupCommands) + { + if (!ExecuteCommand(interpreter, command)) + { + keepRunning = false; + break; + } + } + + if (!batch) + { + std::string command; + while (keepRunning && std::getline(std::cin, command)) + { + keepRunning = ExecuteCommand(interpreter, command); + } + } + + lldb::SBDebugger::Destroy(debugger); + lldb::SBDebugger::Terminate(); + return 0; +} diff --git a/src/SOS/lldbplugin/services.cpp b/src/SOS/lldbplugin/services.cpp index 66054f4e72..110fc7df69 100644 --- a/src/SOS/lldbplugin/services.cpp +++ b/src/SOS/lldbplugin/services.cpp @@ -2293,6 +2293,7 @@ class ExtensionCommand : public lldb::SBCommandPluginInterface char** arguments, lldb::SBCommandReturnObject &result) { + LLDBServices::CurrentResultScope resultScope(g_services, &result); IHostServices* hostservices = GetHostServices(); if (hostservices == nullptr) { @@ -3154,6 +3155,8 @@ LLDBServices::ExecuteCommand( char** arguments, lldb::SBCommandReturnObject &result) { + CurrentResultScope resultScope(this, &result); + // Build all the possible arguments into a string std::string commandArguments; for (const char* arg = *arguments; arg != nullptr; arg = *(++arguments)) diff --git a/src/SOS/lldbplugin/services.h b/src/SOS/lldbplugin/services.h index d9b10ebcb6..12560a180b 100644 --- a/src/SOS/lldbplugin/services.h +++ b/src/SOS/lldbplugin/services.h @@ -72,6 +72,28 @@ class LLDBServices : public ILLDBServices, public ILLDBServices2, public IDebugg lldb::SBFrame GetCurrentFrame(); public: + class CurrentResultScope + { + LLDBServices* m_services; + lldb::SBCommandReturnObject* m_previousResult; + + public: + CurrentResultScope(LLDBServices* services, lldb::SBCommandReturnObject* result) : + m_services(services), + m_previousResult(services->m_currentResult) + { + m_services->m_currentResult = result; + } + + ~CurrentResultScope() + { + m_services->m_currentResult = m_previousResult; + } + + CurrentResultScope(const CurrentResultScope&) = delete; + CurrentResultScope& operator=(const CurrentResultScope&) = delete; + }; + LLDBServices(lldb::SBDebugger debugger); ~LLDBServices(); @@ -455,8 +477,5 @@ class LLDBServices : public ILLDBServices, public ILLDBServices2, public IDebugg bool ExecuteCommand( const char* commandName, char** arguments, lldb::SBCommandReturnObject &result); - void SetCurrentResult(lldb::SBCommandReturnObject *result) { m_currentResult = result; } - void ClearCurrentResult() { m_currentResult = nullptr; } - HRESULT InternalOutputVaList(ULONG mask, PCSTR format, va_list args); }; diff --git a/src/SOS/lldbplugin/soscommand.cpp b/src/SOS/lldbplugin/soscommand.cpp index c64d11ba64..3ce7565f6b 100644 --- a/src/SOS/lldbplugin/soscommand.cpp +++ b/src/SOS/lldbplugin/soscommand.cpp @@ -35,6 +35,7 @@ class sosCommand : public lldb::SBCommandPluginInterface char** arguments, lldb::SBCommandReturnObject &result) { + LLDBServices::CurrentResultScope resultScope(g_services, &result); result.SetStatus(lldb::eReturnStatusSuccessFinishResult); const char* sosCommand = m_command; @@ -76,10 +77,8 @@ class sosCommand : public lldb::SBCommandPluginInterface } } g_services->FlushCheck(); - g_services->SetCurrentResult(&result); const char* sosArgs = str.c_str(); HRESULT hr = commandFunc(g_services, sosArgs); - g_services->ClearCurrentResult(); if (hr != S_OK) { result.SetStatus(lldb::eReturnStatusFailed);