Remote Non-Stop Support

Summary:
 
This patch is the beginnings of support for Non-stop mode in the remote protocol. Letting a user examine stopped threads, while other threads execute freely.

Non-stop mode is enabled using the setting target.non-stop-mode, which sends a QNonStop packet when establishing the remote connection.
Changes are also made to treat the '?' stop reply packet differently in non-stop mode, according to spec https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Non_002dStop.html#Remote-Non_002dStop.
A setting for querying the remote for default thread on setup is also included.

Handling of '%' async notification packets will be added next.

Reviewers: clayborg

Subscribers: lldb-commits, ADodds, ted, deepak2427

Differential Revision: http://reviews.llvm.org/D9656

llvm-svn: 237239
This commit is contained in:
Ewan Crawford 2015-05-13 09:18:18 +00:00
parent acf20fa233
commit 78baa19781
10 changed files with 128 additions and 8 deletions

View File

@ -189,6 +189,9 @@ public:
void
SetUserSpecifiedTrapHandlerNames (const Args &args);
bool
GetNonStopModeEnabled () const;
bool
GetDisplayRuntimeSupportValues () const;

View File

@ -433,6 +433,12 @@ public:
m_step_in_avoid_no_debug = eLazyBoolCalculate;
m_step_out_avoid_no_debug = eLazyBoolCalculate;
m_run_mode = eOnlyDuringStepping;
// Check if we are in Non-Stop mode
lldb::TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget();
if (target_sp.get() != nullptr && target_sp->GetNonStopModeEnabled())
m_run_mode = eOnlyThisThread;
m_avoid_regexp.clear();
m_step_in_target.clear();
m_class_name.clear();

View File

@ -424,6 +424,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri
content_length = total_length = 1; // The command is one byte long...
break;
case '%': // Async notify packet
case '$':
// Look for a standard gdb packet?
{
@ -466,6 +467,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri
case '+':
case '-':
case '\x03':
case '%':
case '$':
done = true;
break;
@ -586,7 +588,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri
}
}
if (m_bytes[0] == '$')
if (m_bytes[0] == '$' || m_bytes[0] == '%')
{
assert (checksum_idx < m_bytes.size());
if (::isxdigit (m_bytes[checksum_idx+0]) ||

View File

@ -1620,6 +1620,22 @@ GDBRemoteCommunicationClient::GetGDBServerProgramVersion()
return 0;
}
bool
GDBRemoteCommunicationClient::GetDefaultThreadId (lldb::tid_t &tid)
{
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse("qC",response,false) != PacketResult::Success)
return false;
if (!response.IsNormalResponse())
return false;
if (response.GetChar() == 'Q' && response.GetChar() == 'C')
tid = response.GetHexMaxU32(true, -1);
return true;
}
bool
GDBRemoteCommunicationClient::GetHostInfo (bool force)
{
@ -2759,6 +2775,25 @@ GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name)
return false;
}
bool
GDBRemoteCommunicationClient::SetNonStopMode (const bool enable)
{
// Form non-stop packet request
char packet[32];
const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable);
assert(packet_len < (int)sizeof(packet));
StringExtractorGDBRemote response;
// Send to target
if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success)
if (response.IsOKResponse())
return true;
// Failed or not supported
return false;
}
void
GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets)
{

View File

@ -318,6 +318,9 @@ public:
bool
GetHostInfo (bool force = false);
bool
GetDefaultThreadId (lldb::tid_t &tid);
bool
GetOSVersion (uint32_t &major,
@ -393,6 +396,9 @@ public:
lldb::addr_t addr, // Address of breakpoint or watchpoint
uint32_t length); // Byte Size of breakpoint or watchpoint
bool
SetNonStopMode (const bool enable);
void
TestPacketSpeed (const uint32_t num_packets);

View File

@ -391,7 +391,8 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
m_waiting_for_attach (false),
m_destroy_tried_resuming (false),
m_command_sp (),
m_breakpoint_pc_offset (0)
m_breakpoint_pc_offset (0),
m_initial_tid (LLDB_INVALID_THREAD_ID)
{
m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit");
m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue");
@ -769,8 +770,13 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url)
// We have a valid process
SetID (pid);
GetThreadList();
if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false) == GDBRemoteCommunication::PacketResult::Success)
if (m_gdb_comm.GetStopReply(m_last_stop_packet))
{
// '?' Packets must be handled differently in non-stop mode
if (GetTarget().GetNonStopModeEnabled())
HandleStopReplySequence();
if (!m_target.GetArchitecture().IsValid())
{
if (m_gdb_comm.GetProcessArchitecture().IsValid())
@ -1052,8 +1058,13 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info)
return error;
}
if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false) == GDBRemoteCommunication::PacketResult::Success)
if (m_gdb_comm.GetStopReply(m_last_stop_packet))
{
// '?' Packets must be handled differently in non-stop mode
if (GetTarget().GetNonStopModeEnabled())
HandleStopReplySequence();
const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
if (process_arch.IsValid())
@ -1153,12 +1164,22 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url)
error.SetErrorString("not connected to remote gdb server");
return error;
}
// Send $QNonStop:1 packet on startup if required
if (GetTarget().GetNonStopModeEnabled())
m_gdb_comm.SetNonStopMode(true);
m_gdb_comm.GetThreadSuffixSupported ();
m_gdb_comm.GetListThreadsInStopReplySupported ();
m_gdb_comm.GetHostInfo ();
m_gdb_comm.GetVContSupported ('c');
m_gdb_comm.GetVAttachOrWaitSupported();
// Ask the remote server for the default thread id
if (GetTarget().GetNonStopModeEnabled())
m_gdb_comm.GetDefaultThreadId(m_initial_tid);
size_t num_cmds = GetExtraStartupCommands().GetArgumentCount();
for (size_t idx = 0; idx < num_cmds; idx++)
{
@ -1430,11 +1451,12 @@ ProcessGDBRemote::DoResume ()
bool continue_packet_error = false;
if (m_gdb_comm.HasAnyVContSupport ())
{
if (m_continue_c_tids.size() == num_threads ||
if (!GetTarget().GetNonStopModeEnabled() &&
(m_continue_c_tids.size() == num_threads ||
(m_continue_c_tids.empty() &&
m_continue_C_tids.empty() &&
m_continue_s_tids.empty() &&
m_continue_S_tids.empty()))
m_continue_S_tids.empty())))
{
// All threads are continuing, just send a "c" packet
continue_packet.PutCString ("c");
@ -1661,6 +1683,27 @@ ProcessGDBRemote::DoResume ()
return error;
}
void
ProcessGDBRemote::HandleStopReplySequence ()
{
while(true)
{
// Send vStopped
StringExtractorGDBRemote response;
m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false);
// OK represents end of signal list
if (response.IsOKResponse())
break;
// If not OK or a normal packet we have a problem
if (!response.IsNormalResponse())
break;
SetLastStopPacket(response);
}
}
void
ProcessGDBRemote::ClearThreadIDList ()
{
@ -2095,6 +2138,13 @@ ProcessGDBRemote::RefreshStateAfterStop ()
UpdateThreadIDList();
}
// If we have queried for a default thread id
if (m_initial_tid != LLDB_INVALID_THREAD_ID)
{
m_thread_list.SetSelectedThreadByID(m_initial_tid);
m_initial_tid = LLDB_INVALID_THREAD_ID;
}
// Let all threads recover from stopping and do any clean up based
// on the previous thread state (if any).
m_thread_list_real.RefreshStateAfterStop();

View File

@ -358,6 +358,7 @@ protected:
bool m_destroy_tried_resuming;
lldb::CommandObjectSP m_command_sp;
int64_t m_breakpoint_pc_offset;
lldb::tid_t m_initial_tid; // The inital thread ID, given by stub on attach
bool
StartAsyncThread ();
@ -378,6 +379,9 @@ protected:
lldb::StateType
SetThreadStopInfo (StringExtractor& stop_packet);
void
HandleStopReplySequence ();
void
ClearThreadIDList ();

View File

@ -2978,6 +2978,7 @@ g_properties[] =
{ "display-expression-in-crashlogs" , OptionValue::eTypeBoolean , false, false, NULL, NULL, "Expressions that crash will show up in crash logs if the host system supports executable specific crash log strings and this setting is set to true." },
{ "trap-handler-names" , OptionValue::eTypeArray , true, OptionValue::eTypeString, NULL, NULL, "A list of trap handler function names, e.g. a common Unix user process one is _sigtramp." },
{ "display-runtime-support-values" , OptionValue::eTypeBoolean , false, false, NULL, NULL, "If true, LLDB will show variables that are meant to support the operation of a language's runtime support." },
{ "non-stop-mode" , OptionValue::eTypeBoolean , false, 0, NULL, NULL, "Disable lock-step debugging, instead control threads independently." },
{ NULL , OptionValue::eTypeInvalid , false, 0 , NULL, NULL, NULL }
};
@ -3016,7 +3017,8 @@ enum
ePropertyMemoryModuleLoadLevel,
ePropertyDisplayExpressionsInCrashlogs,
ePropertyTrapHandlerNames,
ePropertyDisplayRuntimeSupportValues
ePropertyDisplayRuntimeSupportValues,
ePropertyNonStopModeEnabled
};
@ -3515,6 +3517,13 @@ TargetProperties::SetDisplayRuntimeSupportValues (bool b)
m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
}
bool
TargetProperties::GetNonStopModeEnabled () const
{
const uint32_t idx = ePropertyNonStopModeEnabled;
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, false);
}
const ProcessLaunchInfo &
TargetProperties::GetProcessLaunchInfo ()
{

View File

@ -64,6 +64,10 @@ StringExtractorGDBRemote::GetServerPacketType () const
const char *packet_cstr = m_packet.c_str();
switch (m_packet[0])
{
case '%':
return eServerPacketType_notify;
case '\x03':
if (packet_size == 1) return eServerPacketType_interrupt;
break;

View File

@ -145,6 +145,7 @@ public:
eServerPacketType__M,
eServerPacketType__m,
eServerPacketType_notify, // '%' notification
};
ServerPacketType