summaryrefslogtreecommitdiffstats
path: root/lldb/source
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/API/SBThread.cpp4
-rw-r--r--lldb/source/Core/StructuredData.cpp4
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp27
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h7
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp617
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h17
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp7
-rw-r--r--lldb/source/Target/Memory.cpp139
8 files changed, 577 insertions, 245 deletions
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 78f57f52102..dfc7ce9629f 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -114,13 +114,13 @@ SBThread::GetQueue () const
else
{
if (log)
- log->Printf ("SBThread(%p)::GetQueueKind() => error: process is running",
+ log->Printf ("SBThread(%p)::GetQueue() => error: process is running",
static_cast<void*>(exe_ctx.GetThreadPtr()));
}
}
if (log)
- log->Printf ("SBThread(%p)::GetQueueKind () => SBQueue(%p)",
+ log->Printf ("SBThread(%p)::GetQueue () => SBQueue(%p)",
static_cast<void*>(exe_ctx.GetThreadPtr()), static_cast<void*>(queue_sp.get()));
return sb_queue;
diff --git a/lldb/source/Core/StructuredData.cpp b/lldb/source/Core/StructuredData.cpp
index 9286c3a2991..a2c440948af 100644
--- a/lldb/source/Core/StructuredData.cpp
+++ b/lldb/source/Core/StructuredData.cpp
@@ -298,6 +298,10 @@ StructuredData::ParseJSON (std::string json_text)
{
object_sp = read_json_object (&c);
}
+ else if (*c == '[')
+ {
+ object_sp = read_json_array (&c);
+ }
else
{
// We have bad characters here, this is likely an illegal JSON string.
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index d2a15b3152e..95eebe66223 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -98,6 +98,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() :
m_supports_z4 (true),
m_supports_QEnvironment (true),
m_supports_QEnvironmentHexEncoded (true),
+ m_supports_jThreadsInfo (true),
m_curr_pid (LLDB_INVALID_PROCESS_ID),
m_curr_tid (LLDB_INVALID_THREAD_ID),
m_curr_tid_run (LLDB_INVALID_THREAD_ID),
@@ -601,6 +602,32 @@ GDBRemoteCommunicationClient::GetpPacketSupported (lldb::tid_t tid)
return m_supports_p;
}
+StructuredData::ObjectSP
+GDBRemoteCommunicationClient::GetThreadsInfo()
+{
+ // Get information on all threads at one using the "jThreadsInfo" packet
+ StructuredData::ObjectSP object_sp;
+
+ if (m_supports_jThreadsInfo)
+ {
+ StringExtractorGDBRemote response;
+ m_supports_jThreadExtendedInfo = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success)
+ {
+ if (response.IsUnsupportedResponse())
+ {
+ m_supports_jThreadsInfo = false;
+ }
+ else if (!response.Empty())
+ {
+ object_sp = StructuredData::ParseJSON (response.GetStringRef());
+ }
+ }
+ }
+ return object_sp;
+}
+
+
bool
GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported ()
{
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index ba34a313090..b9cea5f3c67 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -17,6 +17,7 @@
// Other libraries and framework includes
// Project includes
#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/StructuredData.h"
#include "lldb/Target/Process.h"
#include "GDBRemoteCommunication.h"
@@ -537,6 +538,9 @@ public:
bool
AvoidGPackets(ProcessGDBRemote *process);
+ StructuredData::ObjectSP
+ GetThreadsInfo();
+
bool
GetThreadExtendedInfoSupported();
@@ -615,7 +619,8 @@ protected:
m_supports_z3:1,
m_supports_z4:1,
m_supports_QEnvironment:1,
- m_supports_QEnvironmentHexEncoded:1;
+ m_supports_QEnvironmentHexEncoded:1,
+ m_supports_jThreadsInfo:1;
lldb::pid_t m_curr_pid;
lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 64972dfda36..d50ec0e2384 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -378,6 +378,7 @@ 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_continue_c_tids (),
m_continue_C_tids (),
m_continue_s_tids (),
@@ -1784,6 +1785,356 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new
return true;
}
+bool
+ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread)
+{
+ // See if we got thread stop infos for all threads via the "jThreadsInfo" packet
+ if (m_threads_info_sp)
+ {
+ StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray();
+ if (thread_infos)
+ {
+ lldb::tid_t tid;
+ 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)
+ {
+ if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid, LLDB_INVALID_THREAD_ID))
+ {
+ if (tid == thread->GetID())
+ return SetThreadStopInfo(thread_dict);
+ }
+ }
+ }
+ }
+ }
+
+ // Fall back to using the qThreadStopInfo packet
+ StringExtractorGDBRemote stop_packet;
+ if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet))
+ return SetThreadStopInfo (stop_packet) == eStateStopped;
+ return false;
+}
+
+
+ThreadSP
+ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
+ uint8_t signo,
+ const std::string &thread_name,
+ const std::string &reason,
+ const std::string &description,
+ uint32_t exc_type,
+ const std::vector<addr_t> &exc_data,
+ addr_t thread_dispatch_qaddr)
+{
+ ThreadSP thread_sp;
+ if (tid != LLDB_INVALID_THREAD_ID)
+ {
+ // Scope for "locker" below
+ {
+ // m_thread_list_real does have its own mutex, but we need to
+ // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...)
+ // and the m_thread_list_real.AddThread(...) so it doesn't change on us
+ Mutex::Locker locker (m_thread_list_real.GetMutex ());
+ thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
+
+ if (!thread_sp)
+ {
+ // Create the thread if we need to
+ thread_sp.reset (new ThreadGDBRemote (*this, tid));
+ m_thread_list_real.AddThread(thread_sp);
+ }
+ }
+
+ if (thread_sp)
+ {
+ ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+
+ // 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);
+ if (exc_type != 0)
+ {
+ 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));
+ }
+ else
+ {
+ bool handled = false;
+ bool did_exec = false;
+ if (!reason.empty())
+ {
+ 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;
+ }
+ }
+
+ 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
+ {
+ // 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
+ {
+ thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
+ }
+ }
+ }
+ }
+ }
+ return thread_sp;
+}
+
+StateType
+ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
+{
+ static ConstString g_key_tid("tid");
+ static ConstString g_key_name("name");
+ static ConstString g_key_reason("reason");
+ static ConstString g_key_metype("metype");
+ static ConstString g_key_medata("medata");
+ static ConstString g_key_qaddr("qaddr");
+ static ConstString g_key_registers("registers");
+ static ConstString g_key_memory("memory");
+ static ConstString g_key_address("address");
+ static ConstString g_key_bytes("bytes");
+ static ConstString g_key_description("description");
+
+ // Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint8_t signo = 0;
+ std::string value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ StructuredData::Dictionary *registers_dict = nullptr;
+
+ // Iterate through all of the thread dictionary key/value pairs from the structured data dictionary
+
+ thread_dict->ForEach([this, &tid, &registers_dict, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr](ConstString key, StructuredData::Object* object) -> bool
+ {
+ if (key == g_key_tid)
+ {
+ // thread in big endian hex
+ tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID);
+ }
+ else if (key == g_key_metype)
+ {
+ // exception type in big endian hex
+ exc_type = object->GetIntegerValue(0);
+ }
+ else if (key == g_key_medata)
+ {
+ // exception data in big endian hex
+ StructuredData::Array *array = object->GetAsArray();
+ if (array)
+ {
+ array->ForEach([&exc_data](StructuredData::Object* object) -> bool {
+ exc_data.push_back(object->GetIntegerValue());
+ return true; // Keep iterating through all array items
+ });
+ }
+ }
+ else if (key == g_key_name)
+ {
+ thread_name = std::move(object->GetStringValue());
+ }
+ else if (key == g_key_qaddr)
+ {
+ thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS);
+ }
+ else if (key == g_key_reason)
+ {
+ reason = std::move(object->GetStringValue());
+ }
+ else if (key == g_key_description)
+ {
+ description = std::move(object->GetStringValue());
+ }
+ else if (key == g_key_registers)
+ {
+ registers_dict = object->GetAsDictionary();
+ }
+ else if (key == g_key_memory)
+ {
+ StructuredData::Array *array = object->GetAsArray();
+ if (array)
+ {
+ array->ForEach([this](StructuredData::Object* object) -> bool {
+ StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary();
+ if (mem_cache_dict)
+ {
+ lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+ if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>("address", mem_cache_addr))
+ {
+ if (mem_cache_addr != LLDB_INVALID_ADDRESS)
+ {
+ StringExtractor bytes;
+ if (mem_cache_dict->GetValueForKeyAsString("bytes", bytes.GetStringRef()))
+ {
+ bytes.SetFilePos(0);
+
+ const size_t byte_size = bytes.GetStringRef().size()/2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
+ }
+ }
+ }
+ }
+ return true; // Keep iterating through all array items
+ });
+ }
+
+ }
+ return true; // Keep iterating through all dictionary key/value pairs
+ });
+
+
+ ThreadSP thread_sp = SetThreadStopInfo (tid, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr);
+ if (thread_sp)
+ {
+ // Process any expedited register values so we don't have to read them with p/P or g/G packets
+ if (registers_dict)
+ {
+ ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+
+ registers_dict->ForEach([gdb_thread](ConstString key, StructuredData::Object* object) -> bool {
+ const uint32_t reg = StringConvert::ToUInt32 (key.GetCString(), UINT32_MAX, 10);
+ if (reg != UINT32_MAX)
+ {
+ StringExtractor reg_value_extractor;
+ reg_value_extractor.GetStringRef() = std::move(object->GetStringValue());
+ gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor);
+ }
+ return true; // Keep iterating through all array items
+ });
+ }
+ return eStateStopped;
+ }
+ return eStateExited;
+}
StateType
ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
@@ -1814,8 +2165,9 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
BuildDynamicRegisterInfo (true);
}
// Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
const uint8_t signo = stop_packet.GetHexU8();
- std::string name;
+ std::string key;
std::string value;
std::string thread_name;
std::string reason;
@@ -1823,48 +2175,26 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
uint32_t exc_type = 0;
std::vector<addr_t> exc_data;
addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
- ThreadSP thread_sp;
- ThreadGDBRemote *gdb_thread = NULL;
-
- while (stop_packet.GetNameColonValue(name, value))
+ typedef std::map<uint32_t, std::string> ExpeditedRegisterMap;
+ ExpeditedRegisterMap expedited_register_map;
+ while (stop_packet.GetNameColonValue(key, value))
{
- if (name.compare("metype") == 0)
+ if (key.compare("metype") == 0)
{
// exception type in big endian hex
exc_type = StringConvert::ToUInt32 (value.c_str(), 0, 16);
}
- else if (name.compare("medata") == 0)
+ else if (key.compare("medata") == 0)
{
// exception data in big endian hex
exc_data.push_back(StringConvert::ToUInt64 (value.c_str(), 0, 16));
}
- else if (name.compare("thread") == 0)
+ else if (key.compare("thread") == 0)
{
// thread in big endian hex
- lldb::tid_t tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
- // m_thread_list_real does have its own mutex, but we need to
- // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...)
- // and the m_thread_list_real.AddThread(...) so it doesn't change on us
- Mutex::Locker locker (m_thread_list_real.GetMutex ());
- thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
-
- if (!thread_sp)
- {
- // Create the thread if we need to
- thread_sp.reset (new ThreadGDBRemote (*this, tid));
- Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD));
- if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
- log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n",
- __FUNCTION__,
- static_cast<void*>(thread_sp.get()),
- thread_sp->GetID());
-
- m_thread_list_real.AddThread(thread_sp);
- }
- gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
-
+ tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
}
- else if (name.compare("threads") == 0)
+ else if (key.compare("threads") == 0)
{
Mutex::Locker locker(m_thread_list_real.GetMutex());
m_thread_ids.clear();
@@ -1886,7 +2216,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
if (tid != LLDB_INVALID_THREAD_ID)
m_thread_ids.push_back (tid);
}
- else if (name.compare("hexname") == 0)
+ else if (key.compare("hexname") == 0)
{
StringExtractor name_extractor;
// Swap "value" over into "name_extractor"
@@ -1895,19 +2225,19 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
name_extractor.GetHexByteString (value);
thread_name.swap (value);
}
- else if (name.compare("name") == 0)
+ else if (key.compare("name") == 0)
{
thread_name.swap (value);
}
- else if (name.compare("qaddr") == 0)
+ else if (key.compare("qaddr") == 0)
{
thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16);
}
- else if (name.compare("reason") == 0)
+ else if (key.compare("reason") == 0)
{
reason.swap(value);
}
- else if (name.compare("description") == 0)
+ else if (key.compare("description") == 0)
{
StringExtractor desc_extractor;
// Swap "value" over into "name_extractor"
@@ -1916,34 +2246,49 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
desc_extractor.GetHexByteString (value);
description.swap(value);
}
- else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1]))
+ else if (key.compare("memory") == 0)
{
- // We have a register number that contains an expedited
- // register value. Lets supply this register to our thread
- // so it won't have to go and read it.
- if (gdb_thread)
+ // Expedited memory. GDB servers can choose to send back expedited memory
+ // that can populate the L1 memory cache in the process so that things like
+ // the frame pointer backchain can be expedited. This will help stack
+ // backtracing be more efficient by not having to send as many memory read
+ // requests down the remote GDB server.
+
+ // Key/value pair format: memory:<addr>=<bytes>;
+ // <addr> is a number whose base will be interpreted by the prefix:
+ // "0x[0-9a-fA-F]+" for hex
+ // "0[0-7]+" for octal
+ // "[1-9]+" for decimal
+ // <bytes> is native endian ASCII hex bytes just like the register values
+ llvm::StringRef value_ref(value);
+ std::pair<llvm::StringRef, llvm::StringRef> pair;
+ pair = value_ref.split('=');
+ if (!pair.first.empty() && !pair.second.empty())
{
- uint32_t reg = StringConvert::ToUInt32 (name.c_str(), UINT32_MAX, 16);
-
- if (reg != UINT32_MAX)
+ std::string addr_str(pair.first.str());
+ const lldb::addr_t mem_cache_addr = StringConvert::ToUInt64(addr_str.c_str(), LLDB_INVALID_ADDRESS, 0);
+ if (mem_cache_addr != LLDB_INVALID_ADDRESS)
{
- StringExtractor reg_value_extractor;
- // Swap "value" over into "reg_value_extractor"
- reg_value_extractor.GetStringRef().swap(value);
- if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor))
- {
- Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'",
- name.c_str(),
- reg,
- reg,
- reg_value_extractor.GetStringRef().c_str(),
- stop_packet.GetStringRef().c_str());
- }
+ StringExtractor bytes;
+ bytes.GetStringRef() = std::move(pair.second.str());
+ const size_t byte_size = bytes.GetStringRef().size()/2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
}
}
}
+ else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1]))
+ {
+ uint32_t reg = StringConvert::ToUInt32 (key.c_str(), UINT32_MAX, 16);
+ if (reg != UINT32_MAX)
+ expedited_register_map[reg] = std::move(value);
+ }
}
+ ThreadSP thread_sp = SetThreadStopInfo (tid, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr);
+
// If the response is old style 'S' packet which does not provide us with thread information
// then update the thread list and choose the first one.
if (!thread_sp)
@@ -1954,156 +2299,23 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
{
Mutex::Locker locker (m_thread_list_real.GetMutex ());
thread_sp = m_thread_list_real.FindThreadByProtocolID (m_thread_ids.front (), false);
- if (thread_sp)
- gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get ());
}
}
- if (thread_sp)
+ if (thread_sp && !expedited_register_map.empty())
{
- // Clear the stop info just in case we don't set it to anything
- thread_sp->SetStopInfo (StopInfoSP());
-
- gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
- gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str());
- if (exc_type != 0)
- {
- 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));
- }
- else
+ // Process any expedited register values so we don't have to read them with p/P or g/G packets
+ ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+ for (const auto &pair : expedited_register_map)
{
- bool handled = false;
- bool did_exec = false;
- if (!reason.empty())
+ StringExtractor reg_value_extractor;
+ reg_value_extractor.GetStringRef() = pair.second;
+ if (!gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor))
{
- 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;
- }
- }
-
- 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
- {
- // 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
- {
- thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
- }
+ Host::SetCrashDescriptionWithFormat("Setting thread register '0x%2.2x' with value '%s' for stop packet: '%s'",
+ pair.first,
+ reg_value_extractor.GetStringRef().c_str(),
+ stop_packet.GetStringRef().c_str());
}
}
}
@@ -2163,6 +2375,11 @@ 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();
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 02f449ffca4..29ca17ef9cb 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -323,6 +323,9 @@ protected:
void
GetMaxMemorySize();
+ bool
+ CalculateThreadStopInfo (ThreadGDBRemote *thread);
+
//------------------------------------------------------------------
/// Broadcaster event bits definitions.
//------------------------------------------------------------------
@@ -346,6 +349,7 @@ protected:
typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection;
typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
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
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
@@ -379,6 +383,19 @@ protected:
lldb::StateType
SetThreadStopInfo (StringExtractor& stop_packet);
+ lldb::StateType
+ SetThreadStopInfo (StructuredData::Dictionary *thread_dict);
+
+ lldb::ThreadSP
+ SetThreadStopInfo (lldb::tid_t tid,
+ uint8_t signo,
+ const std::string &thread_name,
+ const std::string &reason,
+ const std::string &description,
+ uint32_t exc_type,
+ const std::vector<lldb::addr_t> &exc_data,
+ lldb::addr_t thread_dispatch_qaddr);
+
void
HandleStopReplySequence ();
diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
index e4108edc03f..74b51fd917c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
@@ -284,12 +284,7 @@ ThreadGDBRemote::CalculateStopInfo ()
{
ProcessSP process_sp (GetProcess());
if (process_sp)
- {
- StringExtractorGDBRemote stop_packet;
- ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
- if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet))
- return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped;
- }
+ return static_cast<ProcessGDBRemote *>(process_sp.get())->CalculateThreadStopInfo(this);
return false;
}
diff --git a/lldb/source/Target/Memory.cpp b/lldb/source/Target/Memory.cpp
index 934bc967b68..e61b3ab91e7 100644
--- a/lldb/source/Target/Memory.cpp
+++ b/lldb/source/Target/Memory.cpp
@@ -14,8 +14,9 @@
// Other libraries and framework includes
// Project includes
#include "lldb/Core/DataBufferHeap.h"
-#include "lldb/Core/State.h"
#include "lldb/Core/Log.h"
+#include "lldb/Core/RangeMap.h"
+#include "lldb/Core/State.h"
#include "lldb/Target/Process.h"
using namespace lldb;
@@ -25,11 +26,12 @@ using namespace lldb_private;
// MemoryCache constructor
//----------------------------------------------------------------------
MemoryCache::MemoryCache(Process &process) :
- m_process (process),
- m_cache_line_byte_size (process.GetMemoryCacheLineSize()),
m_mutex (Mutex::eMutexTypeRecursive),
- m_cache (),
- m_invalid_ranges ()
+ m_L1_cache (),
+ m_L2_cache (),
+ m_invalid_ranges (),
+ m_process (process),
+ m_L2_cache_line_byte_size (process.GetMemoryCacheLineSize())
{
}
@@ -44,10 +46,24 @@ void
MemoryCache::Clear(bool clear_invalid_ranges)
{
Mutex::Locker locker (m_mutex);
- m_cache.clear();
+ m_L1_cache.clear();
+ m_L2_cache.clear();
if (clear_invalid_ranges)
m_invalid_ranges.Clear();
- m_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+ m_L2_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+}
+
+void
+MemoryCache::AddL1CacheData(lldb::addr_t addr, const void *src, size_t src_len)
+{
+ AddL1CacheData(addr,DataBufferSP (new DataBufferHeap(DataBufferHeap(src, src_len))));
+}
+
+void
+MemoryCache::AddL1CacheData(lldb::addr_t addr, const DataBufferSP &data_buffer_sp)
+{
+ Mutex::Locker locker (m_mutex);
+ m_L1_cache[addr] = data_buffer_sp;
}
void
@@ -57,29 +73,44 @@ MemoryCache::Flush (addr_t addr, size_t size)
return;
Mutex::Locker locker (m_mutex);
- if (m_cache.empty())
- return;
- const uint32_t cache_line_byte_size = m_cache_line_byte_size;
- const addr_t end_addr = (addr + size - 1);
- const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
- const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
- // Watch for overflow where size will cause us to go off the end of the
- // 64 bit address space
- uint32_t num_cache_lines;
- if (last_cache_line_addr >= first_cache_line_addr)
- num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
- else
- num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
-
- uint32_t cache_idx = 0;
- for (addr_t curr_addr = first_cache_line_addr;
- cache_idx < num_cache_lines;
- curr_addr += cache_line_byte_size, ++cache_idx)
+ // Erase any blocks from the L1 cache that intersect with the flush range
+ if (!m_L1_cache.empty())
{
- BlockMap::iterator pos = m_cache.find (curr_addr);
- if (pos != m_cache.end())
- m_cache.erase(pos);
+ AddrRange flush_range(addr, size);
+ BlockMap::iterator pos = m_L1_cache.lower_bound(addr);
+ while (pos != m_L1_cache.end())
+ {
+ AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+ if (!chunk_range.DoesIntersect(flush_range))
+ break;
+ pos = m_L1_cache.erase(pos);
+ }
+ }
+
+ if (!m_L2_cache.empty())
+ {
+ const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
+ const addr_t end_addr = (addr + size - 1);
+ const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
+ const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
+ // Watch for overflow where size will cause us to go off the end of the
+ // 64 bit address space
+ uint32_t num_cache_lines;
+ if (last_cache_line_addr >= first_cache_line_addr)
+ num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
+ else
+ num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
+
+ uint32_t cache_idx = 0;
+ for (addr_t curr_addr = first_cache_line_addr;
+ cache_idx < num_cache_lines;
+ curr_addr += cache_line_byte_size, ++cache_idx)
+ {
+ BlockMap::iterator pos = m_L2_cache.find (curr_addr);
+ if (pos != m_L2_cache.end())
+ m_L2_cache.erase(pos);
+ }
}
}
@@ -122,6 +153,39 @@ MemoryCache::Read (addr_t addr,
{
size_t bytes_left = dst_len;
+ // Check the L1 cache for a range that contain the entire memory read.
+ // If we find a range in the L1 cache that does, we use it. Else we fall
+ // back to reading memory in m_L2_cache_line_byte_size byte sized chunks.
+ // The L1 cache contains chunks of memory that are not required to be
+ // m_L2_cache_line_byte_size bytes in size, so we don't try anything
+ // tricky when reading from them (no partial reads from the L1 cache).
+
+ Mutex::Locker locker(m_mutex);
+ if (!m_L1_cache.empty())
+ {
+ AddrRange read_range(addr, dst_len);
+ BlockMap::iterator pos = m_L1_cache.lower_bound(addr);
+ if (pos != m_L1_cache.end())
+ {
+ AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+ bool match = chunk_range.Contains(read_range);
+ if (!match && pos != m_L1_cache.begin())
+ {
+ --pos;
+ chunk_range.SetRangeBase(pos->first);
+ chunk_range.SetByteSize(pos->second->GetByteSize());
+ match = chunk_range.Contains(read_range);
+ }
+
+ if (match)
+ {
+ memcpy(dst, pos->second->GetBytes() + addr - chunk_range.GetRangeBase(), dst_len);
+ return dst_len;
+ }
+ }
+ }
+
+
// If this memory read request is larger than the cache line size, then
// we (1) try to read as much of it at once as possible, and (2) don't
// add the data to the memory cache. We don't want to split a big read
@@ -129,19 +193,22 @@ MemoryCache::Read (addr_t addr,
// request, it is unlikely that the caller function will ask for the next
// 4 bytes after the large memory read - so there's little benefit to saving
// it in the cache.
- if (dst && dst_len > m_cache_line_byte_size)
+ if (dst && dst_len > m_L2_cache_line_byte_size)
{
- return m_process.ReadMemoryFromInferior (addr, dst, dst_len, error);
+ size_t bytes_read = m_process.ReadMemoryFromInferior (addr, dst, dst_len, error);
+ // Add this non block sized range to the L1 cache if we actually read anything
+ if (bytes_read > 0)
+ AddL1CacheData(addr, dst, bytes_read);
+ return bytes_read;
}
if (dst && bytes_left > 0)
{
- const uint32_t cache_line_byte_size = m_cache_line_byte_size;
+ const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
uint8_t *dst_buf = (uint8_t *)dst;
addr_t curr_addr = addr - (addr % cache_line_byte_size);
addr_t cache_offset = addr - curr_addr;
- Mutex::Locker locker (m_mutex);
-
+
while (bytes_left > 0)
{
if (m_invalid_ranges.FindEntryThatContains(curr_addr))
@@ -150,8 +217,8 @@ MemoryCache::Read (addr_t addr,
return dst_len - bytes_left;
}
- BlockMap::const_iterator pos = m_cache.find (curr_addr);
- BlockMap::const_iterator end = m_cache.end ();
+ BlockMap::const_iterator pos = m_L2_cache.find (curr_addr);
+ BlockMap::const_iterator end = m_L2_cache.end ();
if (pos != end)
{
@@ -208,7 +275,7 @@ MemoryCache::Read (addr_t addr,
if (process_bytes_read != cache_line_byte_size)
data_buffer_heap_ap->SetByteSize (process_bytes_read);
- m_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
+ m_L2_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
// We have read data and put it into the cache, continue through the
// loop again to get the data out of the cache...
}
OpenPOWER on IntegriCloud