More packet performance improvements.

Changed the "jthreads" key/value in the stop reply packets to be "jstopinfo". This JSON only contains threads with valid stop reasons and allows us not to have to ask about other threads via qThreadStopInfo when we are stepping. The "jstopinfo" only gets sent if there are more than one thread since the stop reply packet contains all the info needed for a single thread.

Added a Process::WillPublicStop() in case process subclasses want to do any extra gathering for public stops. For ProcessGDBRemote, we end up sending a jThreadsInfo packet to gather all expedited registers, expedited memory and MacOSX queue information. We only do this for public stops to minimize the packets we send when we have multiple private stops. Multiple private stops happen when a source level single step, step into or step out run the process multiple times while implementing the stepping, and none of these private stops make it out to the UI via notifications because they are private stops. 

llvm-svn: 242593
This commit is contained in:
Greg Clayton 2015-07-17 23:42:28 +00:00
parent 6d1780cfb8
commit 2e309076f2
9 changed files with 321 additions and 208 deletions

View File

@ -1262,6 +1262,26 @@ public:
virtual Error
UnloadImage (uint32_t image_token);
//------------------------------------------------------------------
/// Called when the process is about to broadcast a public stop.
///
/// There are public and private stops. Private stops are when the
/// process is doing things like stepping and the client doesn't
/// need to know about starts and stop that implement a thread plan.
/// Single stepping over a source line in code might end up being
/// implemented by one or more process starts and stops. Public stops
/// are when clients will be notified that the process is stopped.
/// These events typically trigger UI updates (thread stack frames to
/// be displayed, variables to be displayed, and more). This function
/// can be overriden and allows process subclasses to do something
/// before the eBroadcastBitStateChanged event is sent to public
/// clients.
//------------------------------------------------------------------
virtual void
WillPublicStop ()
{
}
//------------------------------------------------------------------
/// Register for process and thread notifications.
///

View File

@ -303,6 +303,9 @@ public:
lldb::StopReason
GetStopReason();
bool
StopInfoIsUpToDate() const;
// This sets the stop reason to a "blank" stop reason, so you can call functions on the thread
// without having the called function run with whatever stop reason you stopped with.
void

View File

@ -376,7 +376,8 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"),
m_async_thread_state_mutex(Mutex::eMutexTypeRecursive),
m_thread_ids (),
m_threads_info_sp (),
m_jstopinfo_sp (),
m_jthreadsinfo_sp (),
m_continue_c_tids (),
m_continue_C_tids (),
m_continue_s_tids (),
@ -792,7 +793,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url)
}
const StateType state = SetThreadStopInfo (response);
if (state == eStateStopped)
if (state != eStateInvalid)
{
SetPrivateState (state);
}
@ -1397,7 +1398,8 @@ ProcessGDBRemote::WillResume ()
m_continue_C_tids.clear();
m_continue_s_tids.clear();
m_continue_S_tids.clear();
m_threads_info_sp.reset();
m_jstopinfo_sp.reset();
m_jthreadsinfo_sp.reset();
return Error();
}
@ -1717,10 +1719,10 @@ ProcessGDBRemote::UpdateThreadIDList ()
{
Mutex::Locker locker(m_thread_list_real.GetMutex());
if (m_threads_info_sp)
if (m_jthreadsinfo_sp)
{
// If we have the JSON threads info, we can get the thread list from that
StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray();
StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
if (thread_infos && thread_infos->GetSize() > 0)
{
m_thread_ids.clear();
@ -1841,13 +1843,14 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new
return true;
}
bool
ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread)
ProcessGDBRemote::GetThreadStopInfoFromJSON (ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp)
{
// See if we got thread stop infos for all threads via the "jThreadsInfo" packet
if (m_threads_info_sp)
if (thread_infos_sp)
{
StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray();
StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray();
if (thread_infos)
{
lldb::tid_t tid;
@ -1860,12 +1863,36 @@ ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread)
if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid, LLDB_INVALID_THREAD_ID))
{
if (tid == thread->GetID())
return SetThreadStopInfo(thread_dict);
return (bool)SetThreadStopInfo(thread_dict);
}
}
}
}
}
return false;
}
bool
ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread)
{
// See if we got thread stop infos for all threads via the "jThreadsInfo" packet
if (GetThreadStopInfoFromJSON (thread, m_jthreadsinfo_sp))
return true;
// See if we got thread stop info for any threads valid stop info reasons threads
// via the "jstopinfo" packet stop reply packet key/value pair?
if (m_jstopinfo_sp)
{
// If we have "jstopinfo" then we have stop descriptions for all threads
// that have stop reasons, and if there is no entry for a thread, then
// it has no stop reason.
thread->GetRegisterContext()->InvalidateIfNeeded(true);
if (!GetThreadStopInfoFromJSON (thread, m_jstopinfo_sp))
{
thread->SetStopInfo (StopInfoSP());
}
return true;
}
// Fall back to using the qThreadStopInfo packet
StringExtractorGDBRemote stop_packet;
@ -1921,8 +1948,6 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor);
}
// Clear the stop info just in case we don't set it to anything
thread_sp->SetStopInfo (StopInfoSP());
thread_sp->SetName (thread_name.empty() ? NULL : thread_name.c_str());
gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
@ -1932,145 +1957,150 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
else
gdb_thread->ClearQueueInfo();
if (exc_type != 0)
// Make sure we update our thread stop reason just once
if (!thread_sp->StopInfoIsUpToDate())
{
const size_t exc_data_size = exc_data.size();
thread_sp->SetStopInfo (StopInfoSP());
thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp,
exc_type,
exc_data_size,
exc_data_size >= 1 ? exc_data[0] : 0,
exc_data_size >= 2 ? exc_data[1] : 0,
exc_data_size >= 3 ? exc_data[2] : 0));
}
else
{
bool handled = false;
bool did_exec = false;
if (!reason.empty())
if (exc_type != 0)
{
if (reason.compare("trace") == 0)
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
handled = true;
}
else if (reason.compare("breakpoint") == 0)
{
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
if (bp_site_sp)
{
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
handled = true;
if (bp_site_sp->ValidForThisThread (thread_sp.get()))
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
}
else
{
StopInfoSP invalid_stop_info_sp;
thread_sp->SetStopInfo (invalid_stop_info_sp);
}
}
}
else if (reason.compare("trap") == 0)
{
// Let the trap just use the standard signal stop reason below...
}
else if (reason.compare("watchpoint") == 0)
{
StringExtractor desc_extractor(description.c_str());
addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
if (wp_addr != LLDB_INVALID_ADDRESS)
{
WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr);
if (wp_sp)
{
wp_sp->SetHardwareIndex(wp_index);
watch_id = wp_sp->GetID();
}
}
if (watch_id == LLDB_INVALID_WATCH_ID)
{
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS));
if (log) log->Printf ("failed to find watchpoint");
}
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
handled = true;
}
else if (reason.compare("exception") == 0)
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str()));
handled = true;
}
else if (reason.compare("exec") == 0)
{
did_exec = true;
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
handled = true;
}
const size_t exc_data_size = exc_data.size();
thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp,
exc_type,
exc_data_size,
exc_data_size >= 1 ? exc_data[0] : 0,
exc_data_size >= 2 ? exc_data[1] : 0,
exc_data_size >= 3 ? exc_data[2] : 0));
}
if (!handled && signo && did_exec == false)
else
{
if (signo == SIGTRAP)
bool handled = false;
bool did_exec = false;
if (!reason.empty())
{
// Currently we are going to assume SIGTRAP means we are either
// hitting a breakpoint or hardware single stepping.
handled = true;
addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset;
lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
if (bp_site_sp)
if (reason.compare("trace") == 0)
{
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
if (bp_site_sp->ValidForThisThread (thread_sp.get()))
thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
handled = true;
}
else if (reason.compare("breakpoint") == 0)
{
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
if (bp_site_sp)
{
if(m_breakpoint_pc_offset != 0)
thread_sp->GetRegisterContext()->SetPC(pc);
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
handled = true;
if (bp_site_sp->ValidForThisThread (thread_sp.get()))
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
}
else
{
StopInfoSP invalid_stop_info_sp;
thread_sp->SetStopInfo (invalid_stop_info_sp);
}
}
}
else if (reason.compare("trap") == 0)
{
// Let the trap just use the standard signal stop reason below...
}
else if (reason.compare("watchpoint") == 0)
{
StringExtractor desc_extractor(description.c_str());
addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
if (wp_addr != LLDB_INVALID_ADDRESS)
{
WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr);
if (wp_sp)
{
wp_sp->SetHardwareIndex(wp_index);
watch_id = wp_sp->GetID();
}
}
if (watch_id == LLDB_INVALID_WATCH_ID)
{
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS));
if (log) log->Printf ("failed to find watchpoint");
}
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
handled = true;
}
else if (reason.compare("exception") == 0)
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str()));
handled = true;
}
else if (reason.compare("exec") == 0)
{
did_exec = true;
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
handled = true;
}
}
if (!handled && signo && did_exec == false)
{
if (signo == SIGTRAP)
{
// Currently we are going to assume SIGTRAP means we are either
// hitting a breakpoint or hardware single stepping.
handled = true;
addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset;
lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
if (bp_site_sp)
{
// If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
// we can just report no reason. We don't need to worry about stepping over the breakpoint here, that
// will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
if (bp_site_sp->ValidForThisThread (thread_sp.get()))
{
if(m_breakpoint_pc_offset != 0)
thread_sp->GetRegisterContext()->SetPC(pc);
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
}
else
{
StopInfoSP invalid_stop_info_sp;
thread_sp->SetStopInfo (invalid_stop_info_sp);
}
}
else
{
StopInfoSP invalid_stop_info_sp;
thread_sp->SetStopInfo (invalid_stop_info_sp);
// If we were stepping then assume the stop was the result of the trace. If we were
// not stepping then report the SIGTRAP.
// FIXME: We are still missing the case where we single step over a trap instruction.
if (thread_sp->GetTemporaryResumeState() == eStateStepping)
thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
else
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str()));
}
}
if (!handled)
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str()));
}
if (!description.empty())
{
lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
if (stop_info_sp)
{
const char *stop_info_desc = stop_info_sp->GetDescription();
if (!stop_info_desc || !stop_info_desc[0])
stop_info_sp->SetDescription (description.c_str());
}
else
{
// If we were stepping then assume the stop was the result of the trace. If we were
// not stepping then report the SIGTRAP.
// FIXME: We are still missing the case where we single step over a trap instruction.
if (thread_sp->GetTemporaryResumeState() == eStateStepping)
thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
else
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str()));
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
}
}
if (!handled)
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str()));
}
if (!description.empty())
{
lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
if (stop_info_sp)
{
const char *stop_info_desc = stop_info_sp->GetDescription();
if (!stop_info_desc || !stop_info_desc[0])
stop_info_sp->SetDescription (description.c_str());
}
else
{
thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
}
}
}
}
@ -2078,7 +2108,7 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
return thread_sp;
}
StateType
lldb::ThreadSP
ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
{
static ConstString g_key_tid("tid");
@ -2245,21 +2275,19 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
return true; // Keep iterating through all dictionary key/value pairs
});
SetThreadStopInfo (tid,
expedited_register_map,
signo,
thread_name,
reason,
description,
exc_type,
exc_data,
thread_dispatch_qaddr,
queue_vars_valid,
queue_name,
queue_kind,
queue_serial);
return eStateExited;
return SetThreadStopInfo (tid,
expedited_register_map,
signo,
thread_name,
reason,
description,
exc_type,
exc_data,
thread_dispatch_qaddr,
queue_vars_valid,
queue_name,
queue_kind,
queue_serial);
}
StateType
@ -2345,7 +2373,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
if (tid != LLDB_INVALID_THREAD_ID)
m_thread_ids.push_back (tid);
}
else if (key.compare("jthreads") == 0)
else if (key.compare("jstopinfo") == 0)
{
StringExtractor json_extractor;
// Swap "value" over into "name_extractor"
@ -2355,7 +2383,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
// This JSON contains thread IDs and thread stop info for all threads.
// It doesn't contain expedited registers, memory or queue info.
m_threads_info_sp = StructuredData::ParseJSON (value);
m_jstopinfo_sp = StructuredData::ParseJSON (value);
}
else if (key.compare("hexname") == 0)
{
@ -2540,11 +2568,6 @@ ProcessGDBRemote::RefreshStateAfterStop ()
m_initial_tid = LLDB_INVALID_THREAD_ID;
}
// Fetch the threads via an efficient packet that gets stop infos for all threads
// only if we have more than one thread
if (m_thread_ids.size() > 1)
m_threads_info_sp = m_gdb_comm.GetThreadsInfo();
// Let all threads recover from stopping and do any clean up based
// on the previous thread state (if any).
m_thread_list_real.RefreshStateAfterStop();
@ -2819,6 +2842,12 @@ ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response)
{
// Lock the thread stack while we access it
Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex);
// We are are not using non-stop mode, there can only be one last stop
// reply packet, so clear the list.
if (GetTarget().GetNonStopModeEnabled() == false)
m_stop_packet_stack.clear();
// Add this stop packet to the stop packet stack
// This stack will get popped and examined when we switch to the
// Stopped state
@ -2826,7 +2855,6 @@ ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response)
}
}
//------------------------------------------------------------------
// Process Queries
//------------------------------------------------------------------
@ -2854,6 +2882,35 @@ ProcessGDBRemote::GetImageInfoAddress()
return addr;
}
void
ProcessGDBRemote::WillPublicStop ()
{
// See if the GDB remote client supports the JSON threads info.
// If so, we gather stop info for all threads, expedited registers,
// expedited memory, runtime queue information (iOS and MacOSX only),
// and more. Expediting memory will help stack backtracing be much
// faster. Expediting registers will make sure we don't have to read
// the thread registers for GPRs.
m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo();
if (m_jthreadsinfo_sp)
{
// Now set the stop info for each thread and also expedite any registers
// and memory that was in the jThreadsInfo response.
StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
if (thread_infos)
{
const size_t n = thread_infos->GetSize();
for (size_t i=0; i<n; ++i)
{
StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary();
if (thread_dict)
SetThreadStopInfo(thread_dict);
}
}
}
}
//------------------------------------------------------------------
// Process Memory
//------------------------------------------------------------------

View File

@ -161,6 +161,9 @@ public:
lldb::addr_t
GetImageInfoAddress() override;
void
WillPublicStop () override;
//------------------------------------------------------------------
// Process Memory
//------------------------------------------------------------------
@ -359,7 +362,8 @@ protected:
typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
typedef std::map<uint32_t, std::string> ExpeditedRegisterMap;
tid_collection m_thread_ids; // Thread IDs for all threads. This list gets updated after stopping
StructuredData::ObjectSP m_threads_info_sp; // Stop info for all threads if "jThreadsInfo" packet is supported
StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads that have valid stop infos
StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited registers and memory for all threads if "jThreadsInfo" packet is supported
tid_collection m_continue_c_tids; // 'c' for continue
tid_sig_collection m_continue_C_tids; // 'C' for continue with signal
tid_collection m_continue_s_tids; // 's' for step
@ -396,7 +400,10 @@ protected:
lldb::StateType
SetThreadStopInfo (StringExtractor& stop_packet);
lldb::StateType
bool
GetThreadStopInfoFromJSON (ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp);
lldb::ThreadSP
SetThreadStopInfo (StructuredData::Dictionary *thread_dict);
lldb::ThreadSP

View File

@ -4735,6 +4735,11 @@ Process::ProcessEventData::DoOnRemoval (Event *event_ptr)
// If we're stopped and haven't restarted, then do the StopInfo actions here:
if (m_state == eStateStopped && ! m_restarted)
{
// Let process subclasses know we are about to do a public stop and
// do anything they might need to in order to speed up register and
// memory accesses.
process_sp->WillPublicStop();
ThreadList &curr_thread_list = process_sp->GetThreadList();
uint32_t num_threads = curr_thread_list.GetSize();
uint32_t idx;

View File

@ -501,6 +501,15 @@ Thread::GetStopReason()
}
bool
Thread::StopInfoIsUpToDate() const
{
ProcessSP process_sp (GetProcess());
if (process_sp)
return m_stop_info_stop_id == process_sp->GetStopID();
else
return true; // Process is no longer around so stop info is always up to date...
}
void
Thread::SetStopInfo (const lldb::StopInfoSP &stop_info_sp)

View File

@ -206,6 +206,13 @@ MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const
{
// Zero out the structure.
memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
if (exc_type == 0)
{
stop_info->reason = eStopTypeInvalid;
return true;
}
// We always stop with a mach exceptions
stop_info->reason = eStopTypeException;
// Save the EXC_XXXX exception type

View File

@ -2736,21 +2736,24 @@ RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid)
ostrm << ';';
}
// Include JSON info that describes the stop reason for all threads
// so when stepping we don't have to query each thread for its stop
// info. We use the new "jthreads" key whose values is hex ascii JSON
// that contains the thread IDs and only the stop info.
const bool queue_info = false;
const bool registers = false;
const bool memory = false;
JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(queue_info, registers, memory);
if (threads_info_sp)
// 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 (numthreads > 1)
{
ostrm << std::hex << "jthreads:";
std::ostringstream json_strm;
threads_info_sp->Dump (json_strm);
append_hexified_string (ostrm, json_strm.str());
ostrm << ';';
const bool threads_with_valid_stop_info_only = true;
JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only);
if (threads_info_sp)
{
ostrm << std::hex << "jstopinfo:";
std::ostringstream json_strm;
threads_info_sp->Dump (json_strm);
append_hexified_string (ostrm, json_strm.str());
ostrm << ';';
}
}
}
@ -4981,7 +4984,7 @@ get_integer_value_for_key_name_from_json (const char *key, const char *json_stri
}
JSONGenerator::ObjectSP
RNBRemote::GetJSONThreadsInfo(bool queue_info, bool registers, bool memory)
RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only)
{
JSONGenerator::ArraySP threads_array_sp;
if (m_ctx.HasValidProcessID())
@ -4997,54 +5000,64 @@ RNBRemote::GetJSONThreadsInfo(bool queue_info, bool registers, bool memory)
struct DNBThreadStopInfo tid_stop_info;
JSONGenerator::DictionarySP thread_dict_sp(new JSONGenerator::Dictionary());
const bool stop_info_valid = DNBThreadGetStopReason (pid, tid, &tid_stop_info);
// If we are doing stop info only, then we only show threads that have a
// valid stop reason
if (threads_with_valid_stop_info_only)
{
if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid)
continue;
}
JSONGenerator::DictionarySP thread_dict_sp(new JSONGenerator::Dictionary());
thread_dict_sp->AddIntegerItem("tid", tid);
std::string reason_value("none");
if (DNBThreadGetStopReason (pid, tid, &tid_stop_info))
if (stop_info_valid)
{
switch (tid_stop_info.reason)
{
case eStopTypeInvalid:
break;
case eStopTypeSignal:
if (tid_stop_info.details.signal.signo != 0)
{
thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo);
reason_value = "signal";
}
break;
case eStopTypeException:
if (tid_stop_info.details.exception.type != 0)
{
reason_value = "exception";
thread_dict_sp->AddIntegerItem("metype", tid_stop_info.details.exception.type);
JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
for (nub_size_t i=0; i<tid_stop_info.details.exception.data_count; ++i)
{
medata_array_sp->AddItem(JSONGenerator::IntegerSP(new JSONGenerator::Integer(tid_stop_info.details.exception.data[i])));
}
thread_dict_sp->AddItem("medata", medata_array_sp);
}
break;
case eStopTypeExec:
reason_value = "exec";
break;
}
if (tid_stop_info.reason == eStopTypeSignal)
{
thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo);
}
else if (tid_stop_info.reason == eStopTypeException && tid_stop_info.details.exception.type != 0)
{
thread_dict_sp->AddIntegerItem("metype", tid_stop_info.details.exception.type);
JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
for (nub_size_t i=0; i<tid_stop_info.details.exception.data_count; ++i)
{
medata_array_sp->AddItem(JSONGenerator::IntegerSP(new JSONGenerator::Integer(tid_stop_info.details.exception.data[i])));
}
thread_dict_sp->AddItem("medata", medata_array_sp);
}
}
thread_dict_sp->AddStringItem("reason", reason_value);
const char *thread_name = DNBThreadGetName (pid, tid);
if (thread_name && thread_name[0])
thread_dict_sp->AddStringItem("name", thread_name);
if (queue_info)
if (threads_with_valid_stop_info_only == false)
{
const char *thread_name = DNBThreadGetName (pid, tid);
if (thread_name && thread_name[0])
thread_dict_sp->AddStringItem("name", thread_name);
thread_identifier_info_data_t thread_ident_info;
if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info))
{
@ -5070,10 +5083,7 @@ RNBRemote::GetJSONThreadsInfo(bool queue_info, bool registers, bool memory)
}
}
}
}
if (registers)
{
DNBRegisterValue reg_value;
if (g_reg_entries != NULL)
@ -5098,10 +5108,7 @@ RNBRemote::GetJSONThreadsInfo(bool queue_info, bool registers, bool memory)
}
thread_dict_sp->AddItem("registers", registers_dict_sp);
}
}
if (memory)
{
// Add expedited stack memory so stack backtracing doesn't need to read anything from the
// frame pointer chain.
StackMemoryMap stack_mmap;
@ -5136,10 +5143,8 @@ RNBRemote::HandlePacket_jThreadsInfo (const char *p)
// If we haven't run the process yet, return an error.
if (m_ctx.HasValidProcessID())
{
const bool queue_info = true;
const bool registers = true;
const bool memory = true;
JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(queue_info, registers, memory);
const bool threads_with_valid_stop_info_only = false;
JSONGenerator::ObjectSP threads_info_sp = GetJSONThreadsInfo(threads_with_valid_stop_info_only);
if (threads_info_sp)
{

View File

@ -398,7 +398,7 @@ protected:
GetDispatchQueueOffsets();
JSONGenerator::ObjectSP
GetJSONThreadsInfo (bool queue_info, bool registers, bool memory);
GetJSONThreadsInfo (bool threads_with_valid_stop_info_only);
RNBContext m_ctx; // process context
RNBSocket m_comm; // communication port