aboutsummaryrefslogtreecommitdiff
path: root/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp')
-rw-r--r--source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp600
1 files changed, 373 insertions, 227 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index c4523252f190..921369c7ef21 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -43,6 +43,8 @@
#include "lldb/Host/common/NativeRegisterContext.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/LLDBAssert.h"
// Project includes
#include "Utility/StringExtractorGDBRemote.h"
@@ -96,20 +98,6 @@ GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
RegisterPacketHandlers();
}
-//----------------------------------------------------------------------
-// Destructor
-//----------------------------------------------------------------------
-GDBRemoteCommunicationServerLLGS::~GDBRemoteCommunicationServerLLGS()
-{
- Mutex::Locker locker (m_debugged_process_mutex);
-
- if (m_debugged_process_sp)
- {
- m_debugged_process_sp->Terminate ();
- m_debugged_process_sp.reset ();
- }
-}
-
void
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
{
@@ -126,7 +114,7 @@ GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,
&GDBRemoteCommunicationServerLLGS::Handle_interrupt);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_m,
- &GDBRemoteCommunicationServerLLGS::Handle_m);
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M,
&GDBRemoteCommunicationServerLLGS::Handle_M);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p,
@@ -161,6 +149,8 @@ GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
&GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo,
&GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_jThreadsInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo,
&GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read,
@@ -175,6 +165,8 @@ GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
&GDBRemoteCommunicationServerLLGS::Handle_vCont);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vCont_actions,
&GDBRemoteCommunicationServerLLGS::Handle_vCont_actions);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_x,
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z,
&GDBRemoteCommunicationServerLLGS::Handle_Z);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z,
@@ -223,6 +215,7 @@ GDBRemoteCommunicationServerLLGS::LaunchProcess ()
error = NativeProcessProtocol::Launch(
m_process_launch_info,
*this,
+ m_mainloop,
m_debugged_process_sp);
}
@@ -274,17 +267,6 @@ GDBRemoteCommunicationServerLLGS::LaunchProcess ()
printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID ());
- // Add to list of spawned processes.
- lldb::pid_t pid;
- if ((pid = m_process_launch_info.GetProcessID ()) != LLDB_INVALID_PROCESS_ID)
- {
- // add to spawned pids
- Mutex::Locker locker (m_spawned_pids_mutex);
- // On an lldb-gdbserver, we would expect there to be only one.
- assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed");
- m_spawned_pids.insert (pid);
- }
-
return error;
}
@@ -297,48 +279,37 @@ GDBRemoteCommunicationServerLLGS::AttachToProcess (lldb::pid_t pid)
if (log)
log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, __FUNCTION__, pid);
- // Scope for mutex locker.
+ // Before we try to attach, make sure we aren't already monitoring something else.
+ if (m_debugged_process_sp && m_debugged_process_sp->GetID() != LLDB_INVALID_PROCESS_ID)
+ return Error("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, m_debugged_process_sp->GetID());
+
+ // Try to attach.
+ error = NativeProcessProtocol::Attach(pid, *this, m_mainloop, m_debugged_process_sp);
+ if (!error.Success ())
{
- // Before we try to attach, make sure we aren't already monitoring something else.
- Mutex::Locker locker (m_spawned_pids_mutex);
- if (!m_spawned_pids.empty ())
- {
- error.SetErrorStringWithFormat ("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, *m_spawned_pids.begin());
- return error;
- }
+ fprintf (stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString ());
+ return error;
+ }
- // Try to attach.
- error = NativeProcessProtocol::Attach(pid, *this, m_debugged_process_sp);
- if (!error.Success ())
- {
- fprintf (stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString ());
+ // Setup stdout/stderr mapping from inferior.
+ auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor ();
+ if (terminal_fd >= 0)
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd);
+ error = SetSTDIOFileDescriptor (terminal_fd);
+ if (error.Fail ())
return error;
- }
-
- // Setup stdout/stderr mapping from inferior.
- auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor ();
- if (terminal_fd >= 0)
- {
- if (log)
- log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd);
- error = SetSTDIOFileDescriptor (terminal_fd);
- if (error.Fail ())
- return error;
- }
- else
- {
- if (log)
- log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd);
- }
-
- printf ("Attached to process %" PRIu64 "...\n", pid);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd);
+ }
- // Add to list of spawned processes.
- assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed");
- m_spawned_pids.insert (pid);
+ printf ("Attached to process %" PRIu64 "...\n", pid);
- return error;
- }
+ return error;
}
void
@@ -455,6 +426,178 @@ WriteRegisterValueInHexFixedWidth (StreamString &response,
}
}
+static JSONObject::SP
+GetRegistersAsJSON(NativeThreadProtocol &thread, bool abridged)
+{
+ Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD));
+
+ NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext ();
+ if (! reg_ctx_sp)
+ return nullptr;
+
+ JSONObject::SP register_object_sp = std::make_shared<JSONObject>();
+
+#ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET
+ // Expedite all registers in the first register set (i.e. should be GPRs) that are not contained in other registers.
+ const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0);
+ if (! reg_set_p)
+ return nullptr;
+ for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p)
+ {
+ uint32_t reg_num = *reg_num_p;
+#else
+ // Expedite only a couple of registers until we figure out why sending registers is
+ // expensive.
+ static const uint32_t k_expedited_registers[] = {
+ LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM
+ };
+ static const uint32_t k_abridged_expedited_registers[] = {
+ LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM
+ };
+
+ for (const uint32_t *generic_reg_p = abridged ? k_abridged_expedited_registers : k_expedited_registers;
+ *generic_reg_p != LLDB_INVALID_REGNUM;
+ ++generic_reg_p)
+ {
+ uint32_t reg_num = reg_ctx_sp->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, *generic_reg_p);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ continue; // Target does not support the given register.
+#endif
+
+ const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info_p == nullptr)
+ {
+ if (log)
+ log->Printf("%s failed to get register info for register index %" PRIu32,
+ __FUNCTION__, reg_num);
+ continue;
+ }
+
+ if (reg_info_p->value_regs != nullptr)
+ continue; // Only expedite registers that are not contained in other registers.
+
+ RegisterValue reg_value;
+ Error error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name : "<unnamed-register>", reg_num,
+ error.AsCString ());
+ continue;
+ }
+
+ StreamString stream;
+ WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, &reg_value);
+
+ register_object_sp->SetObject(std::to_string(reg_num),
+ std::make_shared<JSONString>(stream.GetString()));
+ }
+
+ return register_object_sp;
+}
+
+static const char *
+GetStopReasonString(StopReason stop_reason)
+{
+ switch (stop_reason)
+ {
+ case eStopReasonTrace:
+ return "trace";
+ case eStopReasonBreakpoint:
+ return "breakpoint";
+ case eStopReasonWatchpoint:
+ return "watchpoint";
+ case eStopReasonSignal:
+ return "signal";
+ case eStopReasonException:
+ return "exception";
+ case eStopReasonExec:
+ return "exec";
+ case eStopReasonInstrumentation:
+ case eStopReasonInvalid:
+ case eStopReasonPlanComplete:
+ case eStopReasonThreadExiting:
+ case eStopReasonNone:
+ break; // ignored
+ }
+ return nullptr;
+}
+
+static JSONArray::SP
+GetJSONThreadsInfo(NativeProcessProtocol &process, bool abridged)
+{
+ Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ JSONArray::SP threads_array_sp = std::make_shared<JSONArray>();
+
+ // Ensure we can get info on the given thread.
+ uint32_t thread_idx = 0;
+ for ( NativeThreadProtocolSP thread_sp;
+ (thread_sp = process.GetThreadAtIndex(thread_idx)) != nullptr;
+ ++thread_idx)
+ {
+
+ lldb::tid_t tid = thread_sp->GetID();
+
+ // Grab the reason this thread stopped.
+ struct ThreadStopInfo tid_stop_info;
+ std::string description;
+ if (!thread_sp->GetStopReason (tid_stop_info, description))
+ return nullptr;
+
+ const int signum = tid_stop_info.details.signal.signo;
+ if (log)
+ {
+ log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64,
+ __FUNCTION__,
+ process.GetID (),
+ tid,
+ signum,
+ tid_stop_info.reason,
+ tid_stop_info.details.exception.type);
+ }
+
+ JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>();
+ threads_array_sp->AppendObject(thread_obj_sp);
+
+ if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread_sp, abridged))
+ thread_obj_sp->SetObject("registers", registers_sp);
+
+ thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid));
+ if (signum != 0)
+ thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(signum));
+
+ const std::string thread_name = thread_sp->GetName ();
+ if (! thread_name.empty())
+ thread_obj_sp->SetObject("name", std::make_shared<JSONString>(thread_name));
+
+ if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason))
+ thread_obj_sp->SetObject("reason", std::make_shared<JSONString>(stop_reason_str));
+
+ if (! description.empty())
+ thread_obj_sp->SetObject("description", std::make_shared<JSONString>(description));
+
+ if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type)
+ {
+ thread_obj_sp->SetObject("metype",
+ std::make_shared<JSONNumber>(tid_stop_info.details.exception.type));
+
+ JSONArray::SP medata_array_sp = std::make_shared<JSONArray>();
+ for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i)
+ {
+ medata_array_sp->AppendObject(std::make_shared<JSONNumber>(
+ tid_stop_info.details.exception.data[i]));
+ }
+ thread_obj_sp->SetObject("medata", medata_array_sp);
+ }
+
+ // TODO: Expedite interesting regions of inferior memory
+ }
+
+ return threads_array_sp;
+}
+
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid)
{
@@ -548,6 +691,31 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid)
response.Printf ("%" PRIx64, listed_thread_sp->GetID ());
}
response.PutChar (';');
+
+ // Include JSON info that describes the stop reason for any threads
+ // that actually have stop reasons. We use the new "jstopinfo" key
+ // whose values is hex ascii JSON that contains the thread IDs
+ // thread stop info only for threads that have stop reasons. Only send
+ // this if we have more than one thread otherwise this packet has all
+ // the info it needs.
+ if (thread_index > 0)
+ {
+ const bool threads_with_valid_stop_info_only = true;
+ JSONArray::SP threads_info_sp = GetJSONThreadsInfo(*m_debugged_process_sp,
+ threads_with_valid_stop_info_only);
+ if (threads_info_sp)
+ {
+ response.PutCString("jstopinfo:");
+ StreamString unescaped_response;
+ threads_info_sp->Write(unescaped_response);
+ response.PutCStringAsRawHex8(unescaped_response.GetData());
+ response.PutChar(';');
+ }
+ else if (log)
+ log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to prepare a jstopinfo field for pid %" PRIu64,
+ __FUNCTION__, m_debugged_process_sp->GetID());
+
+ }
}
//
@@ -595,34 +763,7 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid)
}
}
- const char* reason_str = nullptr;
- switch (tid_stop_info.reason)
- {
- case eStopReasonTrace:
- reason_str = "trace";
- break;
- case eStopReasonBreakpoint:
- reason_str = "breakpoint";
- break;
- case eStopReasonWatchpoint:
- reason_str = "watchpoint";
- break;
- case eStopReasonSignal:
- reason_str = "signal";
- break;
- case eStopReasonException:
- reason_str = "exception";
- break;
- case eStopReasonExec:
- reason_str = "exec";
- break;
- case eStopReasonInstrumentation:
- case eStopReasonInvalid:
- case eStopReasonPlanComplete:
- case eStopReasonThreadExiting:
- case eStopReasonNone:
- break;
- }
+ const char* reason_str = GetStopReasonString(tid_stop_info.reason);
if (reason_str != nullptr)
{
response.Printf ("reason:%s;", reason_str);
@@ -663,43 +804,15 @@ GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited (NativeProcessProto
if (log)
log->Printf ("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
- // Send the exit result, and don't flush output.
- // Note: flushing output here would join the inferior stdio reflection thread, which
- // would gunk up the waitpid monitor thread that is calling this.
- PacketResult result = SendStopReasonForState (StateType::eStateExited, false);
+ PacketResult result = SendStopReasonForState(StateType::eStateExited);
if (result != PacketResult::Success)
{
if (log)
log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to send stop notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID ());
}
- // Remove the process from the list of spawned pids.
- {
- Mutex::Locker locker (m_spawned_pids_mutex);
- if (m_spawned_pids.erase (process->GetID ()) < 1)
- {
- if (log)
- log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to remove PID %" PRIu64 " from the spawned pids list", __FUNCTION__, process->GetID ());
-
- }
- }
-
- // FIXME can't do this yet - since process state propagation is currently
- // synchronous, it is running off the NativeProcessProtocol's innards and
- // will tear down the NPP while it still has code to execute.
-#if 0
- // Clear the NativeProcessProtocol pointer.
- {
- Mutex::Locker locker (m_debugged_process_mutex);
- m_debugged_process_sp.reset();
- }
-#endif
-
// Close the pipe to the inferior terminal i/o if we launched it
- // and set one up. Otherwise, 'k' and its flush of stdio could
- // end up waiting on a thread join that will never end. Consider
- // adding a timeout to the connection thread join call so we
- // can avoid that scenario altogether.
+ // and set one up.
MaybeCloseInferiorTerminalConnection ();
// We are ready to exit the debug monitor.
@@ -725,7 +838,7 @@ GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped (NativeProcessProt
break;
default:
// In all other cases, send the stop reason.
- PacketResult result = SendStopReasonForState (StateType::eStateStopped, false);
+ PacketResult result = SendStopReasonForState(StateType::eStateStopped);
if (result != PacketResult::Success)
{
if (log)
@@ -748,21 +861,30 @@ GDBRemoteCommunicationServerLLGS::ProcessStateChanged (NativeProcessProtocol *pr
StateAsCString (state));
}
- // Make sure we get all of the pending stdout/stderr from the inferior
- // and send it to the lldb host before we send the state change
- // notification
- m_stdio_communication.SynchronizeWithReadThread();
-
switch (state)
{
- case StateType::eStateExited:
- HandleInferiorState_Exited (process);
+ case StateType::eStateRunning:
+ StartSTDIOForwarding();
break;
case StateType::eStateStopped:
+ // Make sure we get all of the pending stdout/stderr from the inferior
+ // and send it to the lldb host before we send the state change
+ // notification
+ SendProcessOutput();
+ // Then stop the forwarding, so that any late output (see llvm.org/pr25652) does not
+ // interfere with our protocol.
+ StopSTDIOForwarding();
HandleInferiorState_Stopped (process);
break;
+ case StateType::eStateExited:
+ // Same as above
+ SendProcessOutput();
+ StopSTDIOForwarding();
+ HandleInferiorState_Exited (process);
+ break;
+
default:
if (log)
{
@@ -796,7 +918,6 @@ GDBRemoteCommunicationServerLLGS::DataAvailableCallback ()
if(log)
log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with client failed, exiting",
__FUNCTION__);
- m_read_handle_up.reset();
m_mainloop.RequestTermination();
return;
}
@@ -817,7 +938,6 @@ GDBRemoteCommunicationServerLLGS::DataAvailableCallback ()
if(log)
log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet failed: %s",
__FUNCTION__, error.AsCString());
- m_read_handle_up.reset();
m_mainloop.RequestTermination();
break;
}
@@ -831,7 +951,7 @@ GDBRemoteCommunicationServerLLGS::InitializeConnection (std::unique_ptr<Connecti
GDBRemoteCommunicationServer::SetConnection(connection.release());
Error error;
- m_read_handle_up = m_mainloop.RegisterReadObject(read_object_sp,
+ m_network_handle_up = m_mainloop.RegisterReadObject(read_object_sp,
[this] (MainLoopBase &) { DataAvailableCallback(); }, error);
return error;
}
@@ -857,7 +977,7 @@ GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor (int fd)
{
Error error;
- // Set up the Read Thread for reading/handling process I/O
+ // Set up the reading/handling of process I/O
std::unique_ptr<ConnectionFileDescriptor> conn_up (new ConnectionFileDescriptor (fd, true));
if (!conn_up)
{
@@ -873,29 +993,73 @@ GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor (int fd)
return error;
}
+ return Error();
+}
+
+void
+GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding()
+{
+ // Don't forward if not connected (e.g. when attaching).
+ if (! m_stdio_communication.IsConnected())
+ return;
+
// llgs local-process debugging may specify PTY paths, which will make these
// file actions non-null
// process launch -e/o will also make these file actions non-null
// nullptr means that the traffic is expected to flow over gdb-remote protocol
- if (
- m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr ||
- m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr
- )
+ if ( m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) &&
+ m_process_launch_info.GetFileActionForFD(STDERR_FILENO))
+ return;
+
+ Error error;
+ lldbassert(! m_stdio_handle_up);
+ m_stdio_handle_up = m_mainloop.RegisterReadObject(
+ m_stdio_communication.GetConnection()->GetReadObject(),
+ [this] (MainLoopBase &) { SendProcessOutput(); }, error);
+
+ if (! m_stdio_handle_up)
{
- // output from the process must be forwarded over gdb-remote
- // create a thread to read the handle and send the data
- m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this);
- m_stdio_communication.StartReadThread();
+ // Not much we can do about the failure. Log it and continue without forwarding.
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio forwarding: %s",
+ __FUNCTION__, error.AsCString());
}
+}
- return error;
+void
+GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding()
+{
+ m_stdio_handle_up.reset();
}
void
-GDBRemoteCommunicationServerLLGS::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len)
+GDBRemoteCommunicationServerLLGS::SendProcessOutput()
{
- GDBRemoteCommunicationServerLLGS *server = reinterpret_cast<GDBRemoteCommunicationServerLLGS*> (baton);
- static_cast<void> (server->SendONotification (static_cast<const char *>(src), src_len));
+ char buffer[1024];
+ ConnectionStatus status;
+ Error error;
+ while (true)
+ {
+ size_t bytes_read = m_stdio_communication.Read(buffer, sizeof buffer, 0, status, &error);
+ switch (status)
+ {
+ case eConnectionStatusSuccess:
+ SendONotification(buffer, bytes_read);
+ break;
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusError:
+ case eConnectionStatusNoConnection:
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio forwarding as communication returned status %d (error: %s)", __FUNCTION__, status, error.AsCString());
+ m_stdio_handle_up.reset();
+ return;
+
+ case eConnectionStatusInterrupted:
+ case eConnectionStatusTimedOut:
+ return;
+ }
+ }
}
GDBRemoteCommunication::PacketResult
@@ -941,49 +1105,24 @@ GDBRemoteCommunicationServerLLGS::Handle_qC (StringExtractorGDBRemote &packet)
return SendPacketNoLock (response.GetData(), response.GetSize());
}
-bool
-GDBRemoteCommunicationServerLLGS::DebuggedProcessReaped (lldb::pid_t pid)
-{
- // reap a process that we were debugging (but not debugserver)
- Mutex::Locker locker (m_spawned_pids_mutex);
- return m_spawned_pids.erase(pid) > 0;
-}
-
-bool
-GDBRemoteCommunicationServerLLGS::ReapDebuggedProcess (void *callback_baton,
- lldb::pid_t pid,
- bool exited,
- int signal, // Zero for no signal
- int status) // Exit value of process if signal is zero
-{
- GDBRemoteCommunicationServerLLGS *server = (GDBRemoteCommunicationServerLLGS *)callback_baton;
- server->DebuggedProcessReaped (pid);
- return true;
-}
-
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_k (StringExtractorGDBRemote &packet)
{
- // shutdown all spawned processes
- std::set<lldb::pid_t> spawned_pids_copy;
+ Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
- // copy pids
- {
- Mutex::Locker locker (m_spawned_pids_mutex);
- spawned_pids_copy.insert (m_spawned_pids.begin (), m_spawned_pids.end ());
- }
+ StopSTDIOForwarding();
- // nuke the spawned processes
- for (auto it = spawned_pids_copy.begin (); it != spawned_pids_copy.end (); ++it)
+ if (! m_debugged_process_sp)
{
- lldb::pid_t spawned_pid = *it;
- if (!KillSpawnedProcess (spawned_pid))
- {
- fprintf (stderr, "%s: failed to kill spawned pid %" PRIu64 ", ignoring.\n", __FUNCTION__, spawned_pid);
- }
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s No debugged process found.", __FUNCTION__);
+ return PacketResult::Success;
}
- FlushInferiorOutput ();
+ Error error = m_debugged_process_sp->Kill();
+ if (error.Fail() && log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to kill debugged process %" PRIu64 ": %s",
+ __FUNCTION__, m_debugged_process_sp->GetID(), error.AsCString());
// No OK response for kill packet.
// return SendOKResponse ();
@@ -1316,11 +1455,11 @@ GDBRemoteCommunicationServerLLGS::Handle_stop_reason (StringExtractorGDBRemote &
if (!m_debugged_process_sp)
return SendErrorResponse (02);
- return SendStopReasonForState (m_debugged_process_sp->GetState (), true);
+ return SendStopReasonForState (m_debugged_process_sp->GetState());
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::SendStopReasonForState (lldb::StateType process_state, bool flush_on_exit)
+GDBRemoteCommunicationServerLLGS::SendStopReasonForState (lldb::StateType process_state)
{
Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
@@ -1349,8 +1488,6 @@ GDBRemoteCommunicationServerLLGS::SendStopReasonForState (lldb::StateType proces
case eStateInvalid:
case eStateUnloaded:
case eStateExited:
- if (flush_on_exit)
- FlushInferiorOutput ();
return SendWResponse(m_debugged_process_sp.get());
default:
@@ -1448,8 +1585,8 @@ GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo (StringExtractorGDBRemote
response.PutChar (';');
}
- if (reg_info->kinds[RegisterKind::eRegisterKindGCC] != LLDB_INVALID_REGNUM)
- response.Printf ("gcc:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindGCC]);
+ if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != LLDB_INVALID_REGNUM)
+ response.Printf ("ehframe:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindEHFrame]);
if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM)
response.Printf ("dwarf:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindDWARF]);
@@ -1856,7 +1993,7 @@ GDBRemoteCommunicationServerLLGS::Handle_interrupt (StringExtractorGDBRemote &pa
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_m (StringExtractorGDBRemote &packet)
+GDBRemoteCommunicationServerLLGS::Handle_memory_read(StringExtractorGDBRemote &packet)
{
Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
@@ -1889,7 +2026,7 @@ GDBRemoteCommunicationServerLLGS::Handle_m (StringExtractorGDBRemote &packet)
{
if (log)
log->Printf ("GDBRemoteCommunicationServerLLGS::%s nothing to read: zero-length packet", __FUNCTION__);
- return PacketResult::Success;
+ return SendOKResponse();
}
// Allocate the response buffer.
@@ -1916,8 +2053,16 @@ GDBRemoteCommunicationServerLLGS::Handle_m (StringExtractorGDBRemote &packet)
}
StreamGDBRemote response;
- for (size_t i = 0; i < bytes_read; ++i)
- response.PutHex8(buf[i]);
+ packet.SetFilePos(0);
+ char kind = packet.GetChar('?');
+ if (kind == 'x')
+ response.PutEscapedBytes(buf.data(), byte_count);
+ else
+ {
+ assert(kind == 'm');
+ for (size_t i = 0; i < bytes_read; ++i)
+ response.PutHex8(buf[i]);
+ }
return SendPacketNoLock(response.GetData(), response.GetSize());
}
@@ -2108,6 +2253,7 @@ GDBRemoteCommunicationServerLLGS::Handle_Z (StringExtractorGDBRemote &packet)
bool want_breakpoint = true;
bool want_hardware = false;
+ uint32_t watch_flags = 0;
const GDBStoppointType stoppoint_type =
GDBStoppointType(packet.GetS32 (eStoppointInvalid));
@@ -2118,10 +2264,13 @@ GDBRemoteCommunicationServerLLGS::Handle_Z (StringExtractorGDBRemote &packet)
case eBreakpointHardware:
want_hardware = true; want_breakpoint = true; break;
case eWatchpointWrite:
+ watch_flags = 1;
want_hardware = true; want_breakpoint = false; break;
case eWatchpointRead:
+ watch_flags = 2;
want_hardware = true; want_breakpoint = false; break;
case eWatchpointReadWrite:
+ watch_flags = 3;
want_hardware = true; want_breakpoint = false; break;
case eStoppointInvalid:
return SendIllFormedResponse(packet, "Z packet had invalid software/hardware specifier");
@@ -2161,11 +2310,6 @@ GDBRemoteCommunicationServerLLGS::Handle_Z (StringExtractorGDBRemote &packet)
}
else
{
- uint32_t watch_flags =
- stoppoint_type == eWatchpointWrite
- ? 0x1 // Write
- : 0x3; // ReadWrite
-
// Try to set the watchpoint.
const Error error = m_debugged_process_sp->SetWatchpoint (
addr, size, watch_flags, want_hardware);
@@ -2555,7 +2699,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vAttach (StringExtractorGDBRemote &pack
}
// Notify we attached by sending a stop packet.
- return SendStopReasonForState (m_debugged_process_sp->GetState (), true);
+ return SendStopReasonForState (m_debugged_process_sp->GetState ());
}
GDBRemoteCommunication::PacketResult
@@ -2563,8 +2707,7 @@ GDBRemoteCommunicationServerLLGS::Handle_D (StringExtractorGDBRemote &packet)
{
Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS));
- // Scope for mutex locker.
- Mutex::Locker locker (m_spawned_pids_mutex);
+ StopSTDIOForwarding();
// Fail if we don't have a current process.
if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
@@ -2574,14 +2717,6 @@ GDBRemoteCommunicationServerLLGS::Handle_D (StringExtractorGDBRemote &packet)
return SendErrorResponse (0x15);
}
- if (m_spawned_pids.find(m_debugged_process_sp->GetID ()) == m_spawned_pids.end())
- {
- if (log)
- log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to find PID %" PRIu64 " in spawned pids list",
- __FUNCTION__, m_debugged_process_sp->GetID ());
- return SendErrorResponse (0x1);
- }
-
lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
// Consume the ';' after D.
@@ -2603,11 +2738,6 @@ GDBRemoteCommunicationServerLLGS::Handle_D (StringExtractorGDBRemote &packet)
return SendIllFormedResponse (packet, "Invalid pid");
}
- if (m_stdio_communication.IsConnected ())
- {
- m_stdio_communication.StopReadThread ();
- }
-
const Error error = m_debugged_process_sp->Detach ();
if (error.Fail ())
{
@@ -2617,7 +2747,6 @@ GDBRemoteCommunicationServerLLGS::Handle_D (StringExtractorGDBRemote &packet)
return SendErrorResponse (0x01);
}
- m_spawned_pids.erase (m_debugged_process_sp->GetID ());
return SendOKResponse ();
}
@@ -2638,6 +2767,38 @@ GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo (StringExtractorGDBRemo
}
GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo (StringExtractorGDBRemote &)
+{
+ Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a debugged process.
+ if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse (50);
+
+ if (log)
+ log->Printf ("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64,
+ __FUNCTION__, m_debugged_process_sp->GetID());
+
+
+ StreamString response;
+ const bool threads_with_valid_stop_info_only = false;
+ JSONArray::SP threads_array_sp = GetJSONThreadsInfo(*m_debugged_process_sp,
+ threads_with_valid_stop_info_only);
+ if (! threads_array_sp)
+ {
+ if (log)
+ log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to prepare a packet for pid %" PRIu64,
+ __FUNCTION__, m_debugged_process_sp->GetID());
+ return SendErrorResponse(52);
+ }
+
+ threads_array_sp->Write(response);
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock (escaped_response.GetData(), escaped_response.GetSize());
+}
+
+GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet)
{
// Fail if we don't have a current process.
@@ -2686,21 +2847,6 @@ GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress (StringExtractorGDBRem
}
void
-GDBRemoteCommunicationServerLLGS::FlushInferiorOutput ()
-{
- // If we're not monitoring an inferior's terminal, ignore this.
- if (!m_stdio_communication.IsConnected())
- return;
-
- Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
- if (log)
- log->Printf ("GDBRemoteCommunicationServerLLGS::%s() called", __FUNCTION__);
-
- // FIXME implement a timeout on the join.
- m_stdio_communication.JoinReadThread();
-}
-
-void
GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection ()
{
Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));