diff options
author | dim <dim@FreeBSD.org> | 2015-12-30 11:55:28 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2015-12-30 11:55:28 +0000 |
commit | 66b75430a93929d6fc6ed63db14ca28e3ad5b1f6 (patch) | |
tree | 9ed5e1a91f242e2cb5911577356e487a55c01b78 /source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp | |
parent | 23814158e5384f73c6fa951b66d5f807f9c24a2b (diff) | |
download | FreeBSD-src-66b75430a93929d6fc6ed63db14ca28e3ad5b1f6.zip FreeBSD-src-66b75430a93929d6fc6ed63db14ca28e3ad5b1f6.tar.gz |
Vendor import of stripped lldb trunk r256633:
https://llvm.org/svn/llvm-project/lldb/trunk@256633
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp')
-rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp | 305 |
1 files changed, 250 insertions, 55 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp index 1205049..f88ac12 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -15,19 +15,26 @@ // C++ Includes #include <cstring> #include <chrono> +#include <mutex> +#include <sstream> // Other libraries and framework includes +#include "llvm/Support/FileSystem.h" + #include "lldb/Core/Log.h" +#include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/StructuredData.h" #include "lldb/Host/Config.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/JSON.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -40,18 +47,29 @@ using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // GDBRemoteCommunicationServerPlatform constructor //---------------------------------------------------------------------- -GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform() : +GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(const Socket::SocketProtocol socket_protocol, + const char* socket_scheme) : GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"), + m_socket_protocol(socket_protocol), + m_socket_scheme(socket_scheme), + m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), m_platform_sp (Platform::GetHostPlatform ()), m_port_map (), m_port_offset(0) { + m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; + m_pending_gdb_server.port = 0; + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, &GDBRemoteCommunicationServerPlatform::Handle_qC); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, + &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, + &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo, &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, @@ -78,38 +96,16 @@ GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() { } -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +Error +GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& args, + std::string hostname, + lldb::pid_t& pid, + uint16_t& port, + std::string& socket_name) { -#ifdef _WIN32 - return SendErrorResponse(9); -#else - // Spawn a local debugserver as a platform so we can then attach or launch - // a process... - - Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); - if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__); - - // Sleep and wait a bit for debugserver to start to listen... - ConnectionFileDescriptor file_conn; - std::string hostname; - // TODO: /tmp/ should not be hardcoded. User might want to override /tmp - // with the TMPDIR environment variable - packet.SetFilePos(::strlen ("qLaunchGDBServer;")); - std::string name; - std::string value; - uint16_t port = UINT16_MAX; - while (packet.GetNameColonValue(name, value)) - { - if (name.compare ("host") == 0) - hostname.swap(value); - else if (name.compare ("port") == 0) - port = StringConvert::ToUInt32(value.c_str(), 0, 0); - } if (port == UINT16_MAX) port = GetNextAvailablePort(); - + // Spawn a new thread to accept the port that gets bound after // binding to port 0 (zero). @@ -120,6 +116,8 @@ GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGD ProcessLaunchInfo debugserver_launch_info; if (hostname.empty()) hostname = "127.0.0.1"; + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port); @@ -133,53 +131,210 @@ GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGD int platform_port; std::string platform_path; bool ok = UriParser::Parse(GetConnection()->GetURI().c_str(), platform_scheme, platform_ip, platform_port, platform_path); + UNUSED_IF_ASSERT_DISABLED(ok); assert(ok); - Error error = StartDebugserverProcess ( - platform_ip.c_str(), - port, - debugserver_launch_info, - port); - lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + std::ostringstream url; + uint16_t* port_ptr = &port; + if (m_socket_protocol == Socket::ProtocolTcp) + url << platform_ip << ":" << port; + else + { + socket_name = GetDomainSocketPath("gdbserver").GetPath(); + url << socket_name; + port_ptr = nullptr; + } + Error error = StartDebugserverProcess (url.str().c_str(), + nullptr, + debugserver_launch_info, + port_ptr, + args); - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + pid = debugserver_launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) { Mutex::Locker locker (m_spawned_pids_mutex); - m_spawned_pids.insert(debugserver_pid); + m_spawned_pids.insert(pid); if (port > 0) - AssociatePortWithProcess(port, debugserver_pid); + AssociatePortWithProcess(port, pid); } else { if (port > 0) - FreePort (port); + FreePort(port); + } + return error; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + return SendErrorResponse(9); +#else + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__); + + ConnectionFileDescriptor file_conn; + std::string hostname; + packet.SetFilePos(::strlen ("qLaunchGDBServer;")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = StringConvert::ToUInt32(value.c_str(), 0, 0); } - if (error.Success()) + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + std::string socket_name; + Error error = LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name); + if (error.Fail()) { if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); + log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); + return SendErrorResponse(9); + } - char response[256]; - const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); - assert (response_len < (int)sizeof(response)); - PacketResult packet_result = SendPacketNoLock (response, response_len); + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); - if (packet_result != PacketResult::Success) + StreamGDBRemote response; + response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); + if (!socket_name.empty()) + { + response.PutCString("socket_name:"); + response.PutCStringAsRawHex8(socket_name.c_str()); + response.PutChar(';'); + } + + PacketResult packet_result = SendPacketNoLock(response.GetData(), response.GetSize()); + if (packet_result != PacketResult::Success) + { + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + ::kill (debugserver_pid, SIGINT); + } + return packet_result; +#endif +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer (StringExtractorGDBRemote &packet) +{ + if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(4); + + JSONObject::SP server_sp = std::make_shared<JSONObject>(); + server_sp->SetObject("port", std::make_shared<JSONNumber>(m_pending_gdb_server.port)); + if (!m_pending_gdb_server.socket_name.empty()) + server_sp->SetObject("socket_name", + std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str())); + + JSONArray server_list; + server_list.AppendObject(server_sp); + + StreamGDBRemote response; + server_list.Write(response); + + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetData(), escaped_response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); + + lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + + // verify that we know anything about this pid. + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) - ::kill (debugserver_pid, SIGINT); + // not a pid we know about + return SendErrorResponse (10); } - return packet_result; } + + // go ahead and attempt to kill the spawned process + if (KillSpawnedProcess (pid)) + return SendOKResponse (); else + return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) +{ + // make sure we know about this process { - if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return false; } - return SendErrorResponse (9); -#endif + + // first try a SIGTERM (standard kill) + Host::Kill (pid, SIGTERM); + + // check if that worked + for (size_t i=0; i<10; ++i) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + { + // it is now killed + return true; + } + } + usleep (10000); + } + + // check one more time after the final usleep + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // the launched process still lives. Now try killing it again, + // this time with an unblockable signal. + Host::Kill (pid, SIGKILL); + + for (size_t i=0; i<10; ++i) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + { + // it is now killed + return true; + } + } + usleep (10000); + } + + // check one more time after the final usleep + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // no luck - the process still lives + return false; } GDBRemoteCommunication::PacketResult @@ -402,8 +557,48 @@ GDBRemoteCommunicationServerPlatform::FreePortForProcess (lldb::pid_t pid) return false; } +const FileSpec& +GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() +{ + static FileSpec g_domainsocket_dir; + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + const char* domainsocket_dir_env = ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR"); + if (domainsocket_dir_env != nullptr) + g_domainsocket_dir = FileSpec(domainsocket_dir_env, false); + else + HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, g_domainsocket_dir); + }); + + return g_domainsocket_dir; +} + +FileSpec +GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char* prefix) +{ + llvm::SmallString<PATH_MAX> socket_path; + llvm::SmallString<PATH_MAX> socket_name((llvm::StringRef(prefix) + ".%%%%%%").str()); + + FileSpec socket_path_spec(GetDomainSocketDir()); + socket_path_spec.AppendPathComponent(socket_name.c_str()); + + llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path); + return FileSpec(socket_path.c_str(), false); +} + void -GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset) +GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { m_port_offset = port_offset; } + +void +GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(lldb::pid_t pid, + uint16_t port, + const std::string& socket_name) +{ + m_pending_gdb_server.pid = pid; + m_pending_gdb_server.port = port; + m_pending_gdb_server.socket_name = socket_name; +} |