mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-15 12:50:38 +00:00

We have been working on reducing the packet count that is sent between LLDB and the debugserver on MacOSX and iOS. Our approach to this was to reduce the packets required when debugging multiple threads. We currently make one qThreadStopInfoXXXX call (where XXXX is the thread ID in hex) per thread except the thread that stopped with a stop reply packet. In order to implement multiple thread infos in a single reply, we need to use structured data, which means JSON. The new jThreadsInfo packet will attempt to retrieve all thread infos in a single packet. The data is very similar to the stop reply packets, but packaged in JSON and uses JSON arrays where applicable. The JSON output looks like: [ { "tid":1580681, "metype":6, "medata":[2,0], "reason":"exception", "qaddr":140735118423168, "registers": { "0":"8000000000000000", "1":"0000000000000000", "2":"20fabf5fff7f0000", "3":"e8f8bf5fff7f0000", "4":"0100000000000000", "5":"d8f8bf5fff7f0000", "6":"b0f8bf5fff7f0000", "7":"20f4bf5fff7f0000", "8":"8000000000000000", "9":"61a8db78a61500db", "10":"3200000000000000", "11":"4602000000000000", "12":"0000000000000000", "13":"0000000000000000", "14":"0000000000000000", "15":"0000000000000000", "16":"960b000001000000", "17":"0202000000000000", "18":"2b00000000000000", "19":"0000000000000000", "20":"0000000000000000"}, "memory":[ {"address":140734799804592,"bytes":"c8f8bf5fff7f0000c9a59e8cff7f0000"}, {"address":140734799804616,"bytes":"00000000000000000100000000000000"} ] } ] It contains an array of dicitionaries with all of the key value pairs that are normally in the stop reply packet. Including the expedited registers. Notice that is also contains expedited memory in the "memory" key. Any values in this memory will get included in a new L1 cache in lldb_private::Process where if a memory read request is made and that memory request fits into one of the L1 memory cache blocks, it will use that memory data. If a memory request fails in the L1 cache, it will fall back to the L2 cache which is the same block sized caching we were using before these changes. This allows a process to expedite memory that you are likely to use and it reduces packet count. On MacOSX with debugserver, we expedite the frame pointer backchain for a thread (up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and iOS now don't require us to read any memory! We will try these packets out and if successful, we should port these to lldb-server in the near future. <rdar://problem/21494354> llvm-svn: 240354
292 lines
8.6 KiB
C++
292 lines
8.6 KiB
C++
//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#include "ThreadGDBRemote.h"
|
|
|
|
#include "lldb/Breakpoint/Watchpoint.h"
|
|
#include "lldb/Core/ArchSpec.h"
|
|
#include "lldb/Core/DataExtractor.h"
|
|
#include "lldb/Core/State.h"
|
|
#include "lldb/Core/StreamString.h"
|
|
#include "lldb/Target/Platform.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/RegisterContext.h"
|
|
#include "lldb/Target/StopInfo.h"
|
|
#include "lldb/Target/SystemRuntime.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/UnixSignals.h"
|
|
#include "lldb/Target/Unwind.h"
|
|
|
|
#include "ProcessGDBRemote.h"
|
|
#include "ProcessGDBRemoteLog.h"
|
|
#include "Utility/StringExtractorGDBRemote.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::process_gdb_remote;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Thread Registers
|
|
//----------------------------------------------------------------------
|
|
|
|
ThreadGDBRemote::ThreadGDBRemote (Process &process, lldb::tid_t tid) :
|
|
Thread(process, tid),
|
|
m_thread_name (),
|
|
m_dispatch_queue_name (),
|
|
m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS)
|
|
{
|
|
ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)",
|
|
this,
|
|
process.GetID(),
|
|
GetID());
|
|
}
|
|
|
|
ThreadGDBRemote::~ThreadGDBRemote ()
|
|
{
|
|
ProcessSP process_sp(GetProcess());
|
|
ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)",
|
|
this,
|
|
process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID,
|
|
GetID());
|
|
DestroyThread();
|
|
}
|
|
|
|
const char *
|
|
ThreadGDBRemote::GetName ()
|
|
{
|
|
if (m_thread_name.empty())
|
|
return NULL;
|
|
return m_thread_name.c_str();
|
|
}
|
|
|
|
|
|
const char *
|
|
ThreadGDBRemote::GetQueueName ()
|
|
{
|
|
// Always re-fetch the dispatch queue name since it can change
|
|
|
|
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
SystemRuntime *runtime = process_sp->GetSystemRuntime ();
|
|
if (runtime)
|
|
{
|
|
m_dispatch_queue_name = runtime->GetQueueNameFromThreadQAddress (m_thread_dispatch_qaddr);
|
|
}
|
|
if (m_dispatch_queue_name.length() > 0)
|
|
{
|
|
return m_dispatch_queue_name.c_str();
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
queue_id_t
|
|
ThreadGDBRemote::GetQueueID ()
|
|
{
|
|
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
SystemRuntime *runtime = process_sp->GetSystemRuntime ();
|
|
if (runtime)
|
|
{
|
|
return runtime->GetQueueIDFromThreadQAddress (m_thread_dispatch_qaddr);
|
|
}
|
|
}
|
|
}
|
|
return LLDB_INVALID_QUEUE_ID;
|
|
}
|
|
|
|
QueueSP
|
|
ThreadGDBRemote::GetQueue ()
|
|
{
|
|
queue_id_t queue_id = GetQueueID();
|
|
QueueSP queue;
|
|
if (queue_id != LLDB_INVALID_QUEUE_ID)
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
queue = process_sp->GetQueueList().FindQueueByID (queue_id);
|
|
}
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
addr_t
|
|
ThreadGDBRemote::GetQueueLibdispatchQueueAddress ()
|
|
{
|
|
addr_t dispatch_queue_t_addr = LLDB_INVALID_ADDRESS;
|
|
if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
SystemRuntime *runtime = process_sp->GetSystemRuntime ();
|
|
if (runtime)
|
|
{
|
|
dispatch_queue_t_addr = runtime->GetLibdispatchQueueAddressFromThreadQAddress (m_thread_dispatch_qaddr);
|
|
}
|
|
}
|
|
}
|
|
return dispatch_queue_t_addr;
|
|
}
|
|
|
|
StructuredData::ObjectSP
|
|
ThreadGDBRemote::FetchThreadExtendedInfo ()
|
|
{
|
|
StructuredData::ObjectSP object_sp;
|
|
const lldb::user_id_t tid = GetProtocolID();
|
|
Log *log(GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD));
|
|
if (log)
|
|
log->Printf ("Fetching extended information for thread %4.4" PRIx64, tid);
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
|
|
object_sp = gdb_process->GetExtendedInfoForThread (tid);
|
|
}
|
|
return object_sp;
|
|
}
|
|
|
|
void
|
|
ThreadGDBRemote::WillResume (StateType resume_state)
|
|
{
|
|
int signo = GetResumeSignal();
|
|
const lldb::user_id_t tid = GetProtocolID();
|
|
Log *log(GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD));
|
|
if (log)
|
|
log->Printf ("Resuming thread: %4.4" PRIx64 " with state: %s.", tid, StateAsCString(resume_state));
|
|
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
|
|
switch (resume_state)
|
|
{
|
|
case eStateSuspended:
|
|
case eStateStopped:
|
|
// Don't append anything for threads that should stay stopped.
|
|
break;
|
|
|
|
case eStateRunning:
|
|
if (gdb_process->GetUnixSignals().SignalIsValid (signo))
|
|
gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo));
|
|
else
|
|
gdb_process->m_continue_c_tids.push_back(tid);
|
|
break;
|
|
|
|
case eStateStepping:
|
|
if (gdb_process->GetUnixSignals().SignalIsValid (signo))
|
|
gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo));
|
|
else
|
|
gdb_process->m_continue_s_tids.push_back(tid);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ThreadGDBRemote::RefreshStateAfterStop()
|
|
{
|
|
// Invalidate all registers in our register context. We don't set "force" to
|
|
// true because the stop reply packet might have had some register values
|
|
// that were expedited and these will already be copied into the register
|
|
// context by the time this function gets called. The GDBRemoteRegisterContext
|
|
// class has been made smart enough to detect when it needs to invalidate
|
|
// which registers are valid by putting hooks in the register read and
|
|
// register supply functions where they check the process stop ID and do
|
|
// the right thing.
|
|
const bool force = false;
|
|
GetRegisterContext()->InvalidateIfNeeded (force);
|
|
}
|
|
|
|
bool
|
|
ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread)
|
|
{
|
|
return thread != 0;
|
|
}
|
|
|
|
void
|
|
ThreadGDBRemote::Dump(Log *log, uint32_t index)
|
|
{
|
|
}
|
|
|
|
|
|
bool
|
|
ThreadGDBRemote::ShouldStop (bool &step_more)
|
|
{
|
|
return true;
|
|
}
|
|
lldb::RegisterContextSP
|
|
ThreadGDBRemote::GetRegisterContext ()
|
|
{
|
|
if (m_reg_context_sp.get() == NULL)
|
|
m_reg_context_sp = CreateRegisterContextForFrame (NULL);
|
|
return m_reg_context_sp;
|
|
}
|
|
|
|
lldb::RegisterContextSP
|
|
ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame)
|
|
{
|
|
lldb::RegisterContextSP reg_ctx_sp;
|
|
uint32_t concrete_frame_idx = 0;
|
|
|
|
if (frame)
|
|
concrete_frame_idx = frame->GetConcreteFrameIndex ();
|
|
|
|
|
|
if (concrete_frame_idx == 0)
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
{
|
|
ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
|
|
// read_all_registers_at_once will be true if 'p' packet is not supported.
|
|
bool read_all_registers_at_once = !gdb_process->GetGDBRemote().GetpPacketSupported (GetID());
|
|
reg_ctx_sp.reset (new GDBRemoteRegisterContext (*this, concrete_frame_idx, gdb_process->m_register_info, read_all_registers_at_once));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Unwind *unwinder = GetUnwinder ();
|
|
if (unwinder)
|
|
reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame);
|
|
}
|
|
return reg_ctx_sp;
|
|
}
|
|
|
|
bool
|
|
ThreadGDBRemote::PrivateSetRegisterValue (uint32_t reg, StringExtractor &response)
|
|
{
|
|
GDBRemoteRegisterContext *gdb_reg_ctx = static_cast<GDBRemoteRegisterContext *>(GetRegisterContext ().get());
|
|
assert (gdb_reg_ctx);
|
|
return gdb_reg_ctx->PrivateSetRegisterValue (reg, response);
|
|
}
|
|
|
|
bool
|
|
ThreadGDBRemote::CalculateStopInfo ()
|
|
{
|
|
ProcessSP process_sp (GetProcess());
|
|
if (process_sp)
|
|
return static_cast<ProcessGDBRemote *>(process_sp.get())->CalculateThreadStopInfo(this);
|
|
return false;
|
|
}
|
|
|
|
|