Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -492,7 +492,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error); + lldb_private::Error &error, + bool reverse = false); //------------------------------------------------------------------ /// Attach to an existing process using a process ID. Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -1209,7 +1209,7 @@ /// Returns an error object. //------------------------------------------------------------------ virtual Error - ConnectRemote (Stream *strm, const char *remote_url); + ConnectRemote (Stream *strm, const char *remote_url, bool reverse = false); bool GetShouldDetach () const @@ -1461,7 +1461,7 @@ /// Returns an error object. //------------------------------------------------------------------ virtual Error - DoConnectRemote (Stream *strm, const char *remote_url) + DoConnectRemote (Stream *strm, const char *remote_url, bool reverse = false) { Error error; error.SetErrorString ("remote connections are not supported"); Index: source/Commands/CommandObjectProcess.cpp =================================================================== --- source/Commands/CommandObjectProcess.cpp +++ source/Commands/CommandObjectProcess.cpp @@ -1031,7 +1031,7 @@ CommandObjectParsed (interpreter, "process connect", "Connect to a remote debug service.", - "process connect ", + "process connect [reverse]", 0), m_options (interpreter) { @@ -1052,23 +1052,41 @@ bool DoExecute (Args& command, CommandReturnObject &result) override { + bool reverse_connect = false; if (command.GetArgumentCount() != 1) { - result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n", - m_cmd_name.c_str(), - m_cmd_syntax.c_str()); - result.SetStatus (eReturnStatusFailed); - return false; + if (command.GetArgumentCount() != 2) + { + result.AppendErrorWithFormat ("'%s' takes either 1 or 2 arguments:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (strcmp(command.GetArgumentAtIndex(1), "reverse")) + { + result.AppendErrorWithFormat ("Invalid arguments for '%s'.\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + reverse_connect = true; } - - Process *process = m_exe_ctx.GetProcessPtr(); - if (process && process->IsAlive()) + + if (!reverse_connect) { - result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", - process->GetID()); - result.SetStatus (eReturnStatusFailed); - return false; + Process *process = m_exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) + { + result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", + process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; + } } const char *plugin_name = nullptr; @@ -1082,7 +1100,8 @@ plugin_name, debugger, debugger.GetSelectedTarget().get(), - error); + error, + reverse_connect); if (error.Fail() || process_sp == nullptr) { result.AppendError(error.AsCString("Error connecting to the process")); Index: source/Interpreter/CommandInterpreter.cpp =================================================================== --- source/Interpreter/CommandInterpreter.cpp +++ source/Interpreter/CommandInterpreter.cpp @@ -628,14 +628,16 @@ connect_gdb_remote_cmd_ap(new CommandObjectRegexCommand (*this, "gdb-remote", "Connect to a remote GDB server. If no hostname is provided, localhost is assumed.", - "gdb-remote [:]", + "gdb-remote [:] [reverse]", 2, 0, false)); if (connect_gdb_remote_cmd_ap.get()) { if (connect_gdb_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin gdb-remote connect://%1") && - connect_gdb_remote_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1")) + connect_gdb_remote_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1") && + connect_gdb_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+) reverse$", "process connect --plugin gdb-remote connect://%1 reverse") && + connect_gdb_remote_cmd_ap->AddRegexCommand("^([[:digit:]]+) reverse$", "process connect --plugin gdb-remote connect://localhost:%1 reverse")) { CommandObjectSP command_sp(connect_gdb_remote_cmd_ap.release()); m_command_dict[command_sp->GetCommandName ()] = command_sp; Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -44,7 +44,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) override; + lldb_private::Error &error, + bool reverse = false) override; size_t ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error) override; Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -229,7 +229,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) + lldb_private::Error &error, + bool reverse) { // We don't have the pid of the remote gdbserver when it isn't started by us but we still want // to store the list of port forwards we set up in our port forward map. Generate a fake pid for @@ -256,7 +257,8 @@ plugin_name, debugger, target, - error); + error, + reverse); } size_t Index: source/Plugins/Platform/POSIX/PlatformPOSIX.h =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.h +++ source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -187,7 +187,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) override; + lldb_private::Error &error, + bool reverse = false) override; size_t ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error) override; Index: source/Plugins/Platform/POSIX/PlatformPOSIX.cpp =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -991,16 +991,18 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) + lldb_private::Error &error, + bool reverse) { if (m_remote_platform_sp) return m_remote_platform_sp->ConnectProcess(connect_url, plugin_name, debugger, target, - error); + error, + reverse); - return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); + return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error, reverse); } const char* Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -222,7 +222,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) override; + lldb_private::Error &error, + bool reverse = false) override; virtual size_t GetPendingGdbServerList(std::vector& connection_urls); Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -991,14 +991,15 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) + lldb_private::Error &error, + bool reverse) { if (!IsRemote() || !IsConnected()) { error.SetErrorString("Not connected to remote gdb server"); return nullptr; } - return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); + return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error, reverse); } size_t Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -161,6 +161,9 @@ return m_packet_timeout * TimeValue::MicroSecPerSec; } + Error + WaitForDebugserver (const char *url); + //------------------------------------------------------------------ // Start a debugserver instance on the current host using the // supplied connection URL. @@ -339,7 +342,7 @@ DecompressPacket (); Error - StartListenThread (const char *hostname = "127.0.0.1", uint16_t port = 0); + StartListenThread (const char *listen_url = "listen://127.0.0.1:0"); bool JoinListenThread (); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -1066,7 +1066,7 @@ } Error -GDBRemoteCommunication::StartListenThread (const char *hostname, uint16_t port) +GDBRemoteCommunication::StartListenThread (const char *listen_url) { Error error; if (m_listen_thread.IsJoinable()) @@ -1075,11 +1075,6 @@ } else { - char listen_url[512]; - if (hostname && hostname[0]) - snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port); - else - snprintf(listen_url, sizeof(listen_url), "listen://%i", port); m_listen_url = listen_url; SetConnection(new ConnectionFileDescriptor()); m_listen_thread = ThreadLauncher::LaunchThread(listen_url, GDBRemoteCommunication::ListenThread, this, &error); @@ -1112,6 +1107,38 @@ } Error +GDBRemoteCommunication::WaitForDebugserver (const char *url) +{ + Error error; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunication::%s(url=%s)", __FUNCTION__, url ? url : ""); + + std::string listen_url(url); + size_t start_replace = listen_url.find("connect"); + listen_url.replace(start_replace, start_replace + strlen("connect"), "listen"); + + error = StartListenThread (listen_url.c_str()); + if (error.Fail()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() unable to start listen thread: %s", __FUNCTION__, error.AsCString()); + return error; + } + + // Make sure we actually connect with the debugserver... + JoinListenThread(); + + if (error.Fail()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, error.AsCString()); + } + + return error; +} + +Error GDBRemoteCommunication::StartDebugserverProcess (const char *url, Platform *platform, ProcessLaunchInfo &launch_info, @@ -1258,7 +1285,7 @@ { // No host and port given, so lets listen on our end and make the debugserver // connect to us.. - error = StartListenThread ("127.0.0.1", 0); + error = StartListenThread ("listen://127.0.0.1:0"); if (error.Fail()) { if (log) Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -97,7 +97,7 @@ WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) override; Error - DoConnectRemote (Stream *strm, const char *remote_url) override; + DoConnectRemote (Stream *strm, const char *remote_url, bool reverse = false) override; Error WillLaunchOrAttach (); Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -715,7 +715,7 @@ } Error -ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) +ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url, bool reverse) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error (WillLaunchOrAttach ()); @@ -723,8 +723,13 @@ if (error.Fail()) return error; + if (reverse) { + error = m_gdb_comm.WaitForDebugserver(remote_url); + if (error.Fail()) + return error; + } + error = ConnectToDebugserver (remote_url); - if (error.Fail()) return error; StartAsyncThread (); Index: source/Target/Platform.cpp =================================================================== --- source/Target/Platform.cpp +++ source/Target/Platform.cpp @@ -2047,7 +2047,8 @@ const char* plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Error &error) + lldb_private::Error &error, + bool reverse) { error.Clear(); @@ -2074,7 +2075,7 @@ if (!process_sp) return nullptr; - error = process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url); + error = process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url, reverse); if (error.Fail()) return nullptr; Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -3496,7 +3496,7 @@ } Error -Process::ConnectRemote (Stream *strm, const char *remote_url) +Process::ConnectRemote (Stream *strm, const char *remote_url, bool reverse) { m_abi_sp.reset(); m_process_input_reader.reset(); @@ -3504,7 +3504,7 @@ // Find the process and its architecture. Make sure it matches the architecture // of the current Target, and if not adjust it. - Error error (DoConnectRemote (strm, remote_url)); + Error error (DoConnectRemote (strm, remote_url, reverse)); if (error.Success()) { if (GetID() != LLDB_INVALID_PROCESS_ID)