diff options
11 files changed, 583 insertions, 21 deletions
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 075a3fe4628..db0f0cfa028 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1878,7 +1878,13 @@ public: void SendAsyncInterrupt (); - void + //------------------------------------------------------------------ + // Notify this process class that modules got loaded. + // + // If subclasses override this method, they must call this version + // before doing anything in the subclass version of the function. + //------------------------------------------------------------------ + virtual void ModulesDidLoad (ModuleList &module_list); protected: diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index fe88de4c866..f487ae94192 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -33,6 +33,7 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Target/Target.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/UnixSignals.h" @@ -98,6 +99,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : m_supports_z4 (true), m_supports_QEnvironment (true), m_supports_QEnvironmentHexEncoded (true), + m_supports_qSymbol (true), m_curr_pid (LLDB_INVALID_PROCESS_ID), m_curr_tid (LLDB_INVALID_THREAD_ID), m_curr_tid_run (LLDB_INVALID_THREAD_ID), @@ -370,6 +372,7 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings() m_supports_z4 = true; m_supports_QEnvironment = true; m_supports_QEnvironmentHexEncoded = true; + m_supports_qSymbol = true; m_host_arch.Clear(); m_process_arch.Clear(); @@ -4241,3 +4244,87 @@ GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString ob err.Success( ); return true; } + +// Notify the target that gdb is prepared to serve symbol lookup requests. +// packet: "qSymbol::" +// reply: +// OK The target does not need to look up any (more) symbols. +// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex encoded). +// LLDB may provide the value by sending another qSymbol packet +// in the form of"qSymbol:<sym_value>:<sym_name>". + +void +GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process) +{ + if (m_supports_qSymbol) + { + Mutex::Locker locker; + if (GetSequenceMutex(locker, "GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex")) + { + StreamString packet; + packet.PutCString ("qSymbol::"); + while (1) + { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponseNoLock(packet.GetData(), packet.GetSize(), response) == PacketResult::Success) + { + if (response.IsOKResponse()) + { + // We are done serving symbols requests + return; + } + + if (response.IsUnsupportedResponse()) + { + // qSymbol is not supported by the current GDB server we are connected to + m_supports_qSymbol = false; + return; + } + else + { + llvm::StringRef response_str(response.GetStringRef()); + if (response_str.startswith("qSymbol:")) + { + response.SetFilePos(strlen("qSymbol:")); + std::string symbol_name; + if (response.GetHexByteString(symbol_name)) + { + if (symbol_name.empty()) + return; + + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + lldb_private::SymbolContextList sc_list; + if (process->GetTarget().GetImages().FindSymbolsWithNameAndType(ConstString(symbol_name), eSymbolTypeAny, sc_list)) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) + { + if (sc.symbol) + symbol_load_addr = sc.symbol->GetAddress().GetLoadAddress(&process->GetTarget()); + } + } + // This is the normal path where our symbol lookup was successful and we want + // to send a packet with the new symbol value and see if another lookup needs to be + // done. + + // Change "packet" to contain the requested symbol value and name + packet.Clear(); + packet.PutCString("qSymbol:"); + if (symbol_load_addr != LLDB_INVALID_ADDRESS) + packet.Printf("%" PRIx64, symbol_load_addr); + packet.PutCString(":"); + packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); + continue; // go back to the while loop and send "packet" and wait for another response + } + } + } + } + } + // If we make it here, the symbol request packet response wasn't valid or + // our symbol lookup failed so we must abort + return; + + } + } +} + diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 3e5d954c8af..07a3bd93321 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -556,6 +556,9 @@ public: std::string & out, lldb_private::Error & err); + void + ServeSymbolLookups(lldb_private::Process *process); + protected: PacketResult @@ -620,7 +623,8 @@ protected: m_supports_z3:1, m_supports_z4:1, m_supports_QEnvironment:1, - m_supports_QEnvironmentHexEncoded:1; + m_supports_QEnvironmentHexEncoded:1, + m_supports_qSymbol: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 d41bdc70681..c1ad2e69754 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1834,6 +1834,10 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) uint32_t exc_type = 0; std::vector<addr_t> exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid + std::string queue_name; + QueueKind queue_kind = eQueueKindUnknown; + uint64_t queue_serial = 0; ThreadSP thread_sp; ThreadGDBRemote *gdb_thread = NULL; @@ -1914,6 +1918,29 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) { thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16); } + else if (name.compare("qname") == 0) + { + queue_vars_valid = true; + StringExtractor name_extractor; + // Swap "value" over into "name_extractor" + name_extractor.GetStringRef().swap(value); + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString (value); + queue_name.swap (value); + } + else if (name.compare("qkind") == 0) + { + queue_vars_valid = true; + if (value == "serial") + queue_kind = eQueueKindSerial; + else if (value == "concurrent") + queue_kind = eQueueKindConcurrent; + } + else if (name.compare("qserial") == 0) + { + queue_vars_valid = true; + queue_serial = StringConvert::ToUInt64 (value.c_str(), 0, 0); + } else if (name.compare("reason") == 0) { reason.swap(value); @@ -1976,6 +2003,11 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) thread_sp->SetStopInfo (StopInfoSP()); gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); + if (queue_vars_valid) + gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial); + else + gdb_thread->ClearQueueInfo(); + gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str()); if (exc_type != 0) { @@ -4236,6 +4268,19 @@ ProcessGDBRemote::GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb return Error("Unknown error happened during sending the load address packet"); } + +void +ProcessGDBRemote::ModulesDidLoad (ModuleList &module_list) +{ + // We must call the lldb_private::Process::ModulesDidLoad () first before we do anything + Process::ModulesDidLoad (module_list); + + // After loading shared libraries, we can ask our remote GDB server if + // it needs any symbols. + m_gdb_comm.ServeSymbolLookups(this); +} + + class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed { public: diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 174d9e7501f..16e47dfcba6 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -244,6 +244,9 @@ public: Error GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb::addr_t& load_addr) override; + void + ModulesDidLoad (ModuleList &module_list) override; + protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index e4108edc03f..e58121b1978 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -40,9 +40,11 @@ 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) + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_queue_kind(eQueueKindUnknown), + m_queue_serial(0) { - ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, process.GetID(), GetID()); @@ -66,10 +68,36 @@ ThreadGDBRemote::GetName () return m_thread_name.c_str(); } +void +ThreadGDBRemote::ClearQueueInfo () +{ + m_dispatch_queue_name.clear(); + m_queue_kind = eQueueKindUnknown; + m_queue_serial = 0; +} + +void +ThreadGDBRemote::SetQueueInfo (std::string &&queue_name, QueueKind queue_kind, uint64_t queue_serial) +{ + m_dispatch_queue_name = queue_name; + m_queue_kind = queue_kind; + m_queue_serial = queue_serial; +} + const char * ThreadGDBRemote::GetQueueName () { + // If our cached queue info is valid, then someone called ThreadGDBRemote::SetQueueInfo(...) + // with valid information that was gleaned from the stop reply packet. In this case we trust + // that the info is valid in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) + { + if (m_dispatch_queue_name.empty()) + return nullptr; + else + return m_dispatch_queue_name.c_str(); + } // Always re-fetch the dispatch queue name since it can change if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) @@ -79,13 +107,12 @@ ThreadGDBRemote::GetQueueName () { SystemRuntime *runtime = process_sp->GetSystemRuntime (); if (runtime) - { m_dispatch_queue_name = runtime->GetQueueNameFromThreadQAddress (m_thread_dispatch_qaddr); - } - if (m_dispatch_queue_name.length() > 0) - { + else + m_dispatch_queue_name.clear(); + + if (!m_dispatch_queue_name.empty()) return m_dispatch_queue_name.c_str(); - } } } return NULL; @@ -94,6 +121,12 @@ ThreadGDBRemote::GetQueueName () queue_id_t ThreadGDBRemote::GetQueueID () { + // If our cached queue info is valid, then someone called ThreadGDBRemote::SetQueueInfo(...) + // with valid information that was gleaned from the stop reply packet. In this case we trust + // that the info is valid in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) + return m_queue_serial; + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { ProcessSP process_sp (GetProcess()); diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h index 8bff54456dc..175433a3e20 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -91,6 +91,12 @@ public: m_thread_dispatch_qaddr = thread_dispatch_qaddr; } + void + ClearQueueInfo (); + + void + SetQueueInfo (std::string &&queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial); + StructuredData::ObjectSP FetchThreadExtendedInfo () override; @@ -101,13 +107,20 @@ protected: bool PrivateSetRegisterValue (uint32_t reg, StringExtractor &response); - + + bool + CachedQueueInfoIsValid() const + { + return m_queue_kind != lldb::eQueueKindUnknown; + } //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ std::string m_thread_name; std::string m_dispatch_queue_name; lldb::addr_t m_thread_dispatch_qaddr; + lldb::QueueKind m_queue_kind; // Queue info from stop reply/stop info for thread + uint64_t m_queue_serial; // Queue info from stop reply/stop info for thread //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index 4d773221a45..4cde3f87b08 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -1257,6 +1257,86 @@ DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void return 0; } +uint64_t +DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value) +{ + union Integers + { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + }; + + if (integer_size <= sizeof(uint64_t)) + { + Integers ints; + if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) + { + switch (integer_size) + { + case 1: return ints.u8; + case 2: return ints.u16; + case 3: return ints.u32 & 0xffffffu; + case 4: return ints.u32; + case 5: return ints.u32 & 0x000000ffffffffffull; + case 6: return ints.u32 & 0x0000ffffffffffffull; + case 7: return ints.u32 & 0x00ffffffffffffffull; + case 8: return ints.u64; + } + } + } + return fail_value; + +} + +nub_addr_t +DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr) +{ + cpu_type_t cputype = DNBProcessGetCPUType (pid); + if (cputype) + { + const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4; + return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0); + } + return 0; + +} + +std::string +DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr) +{ + std::string cstr; + char buffer[256]; + const nub_size_t max_buffer_cstr_length = sizeof(buffer)-1; + buffer[max_buffer_cstr_length] = '\0'; + nub_size_t length = 0; + nub_addr_t curr_addr = addr; + do + { + nub_size_t bytes_read = DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer); + if (bytes_read == 0) + break; + length = strlen(buffer); + cstr.append(buffer, length); + curr_addr += length; + } while (length == max_buffer_cstr_length); + return cstr; +} + +std::string +DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length) +{ + std::string cstr; + char buffer[fixed_length+1]; + buffer[fixed_length] = '\0'; + nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer); + if (bytes_read > 0) + cstr.assign(buffer); + return cstr; +} + + //---------------------------------------------------------------------- // Write memory to the address space of process PID. This call will take // care of setting and restoring permissions and breaking up the memory diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index e57bb08b678..3f032798f23 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -69,6 +69,10 @@ nub_bool_t DNBProcessInterrupt (nub_process_t pid) DNB_EXPORT; nub_bool_t DNBProcessKill (nub_process_t pid) DNB_EXPORT; nub_bool_t DNBProcessSendEvent (nub_process_t pid, const char *event) DNB_EXPORT; nub_size_t DNBProcessMemoryRead (nub_process_t pid, nub_addr_t addr, nub_size_t size, void *buf) DNB_EXPORT; +uint64_t DNBProcessMemoryReadInteger (nub_process_t pid, nub_addr_t addr, nub_size_t integer_size, uint64_t fail_value) DNB_EXPORT; +nub_addr_t DNBProcessMemoryReadPointer (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; +std::string DNBProcessMemoryReadCString (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; +std::string DNBProcessMemoryReadCStringFixed (nub_process_t pid, nub_addr_t addr, nub_size_t fixed_length) DNB_EXPORT; nub_size_t DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub_size_t size, const void *buf) DNB_EXPORT; nub_addr_t DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions) DNB_EXPORT; nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr) DNB_EXPORT; diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index 2385246a200..1ddf9027427 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -74,14 +74,81 @@ #define INDENT_WITH_TABS(iword_idx) std::setfill('\t') << std::setw((iword_idx)) << "" // Class to handle communications via gdb remote protocol. + +//---------------------------------------------------------------------- +// Decode a single hex character and return the hex value as a number or +// -1 if "ch" is not a hex character. +//---------------------------------------------------------------------- +static inline int +xdigit_to_sint (char ch) +{ + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + if (ch >= 'A' && ch <= 'F') + return 10 + ch - 'A'; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return -1; +} + +//---------------------------------------------------------------------- +// Decode a single hex ASCII byte. Return -1 on failure, a value 0-255 +// on success. +//---------------------------------------------------------------------- +static inline int +decoded_hex_ascii_char(const char *p) +{ + const int hi_nibble = xdigit_to_sint(p[0]); + if (hi_nibble == -1) + return -1; + const int lo_nibble = xdigit_to_sint(p[1]); + if (lo_nibble == -1) + return -1; + return (uint8_t)((hi_nibble << 4) + lo_nibble); +} + +//---------------------------------------------------------------------- +// Decode a hex ASCII string back into a string +//---------------------------------------------------------------------- +static std::string +decode_hex_ascii_string(const char *p, uint32_t max_length = UINT32_MAX) +{ + std::string arg; + if (p) + { + for (const char *c = p; ((c - p)/2) < max_length; c += 2) + { + int ch = decoded_hex_ascii_char(c); + if (ch == -1) + break; + else + arg.push_back(ch); + } + } + return arg; +} + +uint64_t +decode_uint64 (const char *p, int base, char **end = nullptr, uint64_t fail_value = 0) +{ + nub_addr_t addr = strtoull (p, end, 16); + if (addr == 0 && errno != 0) + return fail_value; + return addr; +} + extern void ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args); RNBRemote::RNBRemote () : m_ctx (), m_comm (), + m_arch (), m_continue_thread(-1), m_thread(-1), m_mutex(), + m_dispatch_queue_offsets (), + m_dispatch_queue_offsets_addr (INVALID_NUB_ADDRESS), + m_qSymbol_index (UINT32_MAX), m_packets_recvd(0), m_packets(), m_rx_packets(), @@ -190,11 +257,11 @@ RNBRemote::CreatePacketTable () t.push_back (Packet (query_step_packet_supported, &RNBRemote::HandlePacket_qStepPacketSupported,NULL, "qStepPacketSupported", "Replys with OK if the 's' packet is supported.")); t.push_back (Packet (query_vattachorwait_supported, &RNBRemote::HandlePacket_qVAttachOrWaitSupported,NULL, "qVAttachOrWaitSupported", "Replys with OK if the 'vAttachOrWait' packet is supported.")); t.push_back (Packet (query_sync_thread_state_supported, &RNBRemote::HandlePacket_qSyncThreadStateSupported,NULL, "qSyncThreadStateSupported", "Replys with OK if the 'QSyncThreadState:' packet is supported.")); - t.push_back (Packet (query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); - t.push_back (Packet (query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion, NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); - t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); -// t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups")); - t.push_back (Packet (json_query_thread_extended_info, &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); + t.push_back (Packet (query_host_info, &RNBRemote::HandlePacket_qHostInfo , NULL, "qHostInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); + t.push_back (Packet (query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion , NULL, "qGDBServerVersion", "Replies with multiple 'key:value;' tuples appended to each other.")); + t.push_back (Packet (query_process_info, &RNBRemote::HandlePacket_qProcessInfo , NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other.")); + t.push_back (Packet (query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol , NULL, "qSymbol:", "Notify that host debugger is ready to do symbol lookups")); + t.push_back (Packet (json_query_thread_extended_info,&RNBRemote::HandlePacket_jThreadExtendedInfo , NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information.")); t.push_back (Packet (start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets")); t.push_back (Packet (prefix_reg_packets_with_tid, &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command")); t.push_back (Packet (set_logging_mode, &RNBRemote::HandlePacket_QSetLogging , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix")); @@ -2371,18 +2438,19 @@ RNBRemote::HandlePacket_QSetProcessEvent (const char *p) } void -append_hex_value (std::ostream& ostrm, const uint8_t* buf, size_t buf_size, bool swap) +append_hex_value (std::ostream& ostrm, const void *buf, size_t buf_size, bool swap) { int i; + const uint8_t *p = (const uint8_t *)buf; if (swap) { for (i = static_cast<int>(buf_size)-1; i >= 0; i--) - ostrm << RAWHEX8(buf[i]); + ostrm << RAWHEX8(p[i]); } else { for (i = 0; i < buf_size; i++) - ostrm << RAWHEX8(buf[i]); + ostrm << RAWHEX8(p[i]); } } @@ -2452,6 +2520,45 @@ gdb_regnum_with_fixed_width_hex_register_value (std::ostream& ostrm, } } + +void +RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo (nub_process_t pid, + nub_addr_t dispatch_qaddr, + std::string &queue_name, + uint64_t &queue_width, + uint64_t &queue_serialnum) const +{ + queue_name.clear(); + queue_width = 0; + queue_serialnum = 0; + + if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS && dispatch_qaddr != 0) + { + nub_addr_t dispatch_queue_addr = DNBProcessMemoryReadPointer (pid, dispatch_qaddr); + if (dispatch_queue_addr) + { + queue_width = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_width, dqo_width_size, 0); + queue_serialnum = DNBProcessMemoryReadInteger (pid, dispatch_queue_addr + dqo_serialnum, dqo_serialnum_size, 0); + + if (dqo_version >= 4) + { + // libdispatch versions 4+, pointer to dispatch name is in the + // queue structure. + nub_addr_t pointer_to_label_address = dispatch_queue_addr + dqo_label; + nub_addr_t label_addr = DNBProcessMemoryReadPointer (pid, pointer_to_label_address); + if (label_addr) + queue_name = std::move(DNBProcessMemoryReadCString (pid, label_addr)); + } + else + { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + queue_name = std::move(DNBProcessMemoryReadCStringFixed(pid, dispatch_queue_addr + dqo_label, dqo_label_size)); + } + } + } +} + rnb_err_t RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) { @@ -2468,8 +2575,14 @@ RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) { const bool did_exec = tid_stop_info.reason == eStopTypeExec; if (did_exec) + { RNBRemote::InitializeRegisters(true); + // Reset any symbols that need resetting when we exec + m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS; + m_dispatch_queue_offsets.Clear(); + } + std::ostringstream ostrm; // Output the T packet with the thread ostrm << 'T'; @@ -2522,9 +2635,32 @@ RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info)) { if (thread_ident_info.dispatch_qaddr != 0) - ostrm << std::hex << "qaddr:" << thread_ident_info.dispatch_qaddr << ';'; + { + ostrm << "qaddr:" << std::hex << thread_ident_info.dispatch_qaddr << ';'; + const DispatchQueueOffsets *dispatch_queue_offsets = GetDispatchQueueOffsets(); + if (dispatch_queue_offsets) + { + std::string queue_name; + uint64_t queue_width = 0; + uint64_t queue_serialnum = 0; + dispatch_queue_offsets->GetThreadQueueInfo(pid, thread_ident_info.dispatch_qaddr, queue_name, queue_width, queue_serialnum); + if (!queue_name.empty()) + { + ostrm << "qname:"; + append_hex_value(ostrm, queue_name.data(), queue_name.size(), false); + ostrm << ';'; + } + if (queue_width == 1) + ostrm << "qkind:serial;"; + else if (queue_width > 1) + ostrm << "qkind:concurrent;"; + + if (queue_serialnum > 0) + ostrm << "qserial:" << DECIMAL << queue_serialnum << ';'; + } + } } - + // If a 'QListThreadsInStopReply' was sent to enable this feature, we // will send all thread IDs back in the "threads" key whose value is // a list of hex thread IDs separated by commas: @@ -5003,6 +5139,71 @@ RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p) return SendPacket ("OK"); } + +rnb_err_t +RNBRemote::HandlePacket_qSymbol (const char *command) +{ + const char *p = command; + p += strlen ("qSymbol:"); + const char *sep = strchr(p, ':'); + + std::string symbol_name; + std::string symbol_value_str; + // Extract the symbol value if there is one + if (sep > p) + symbol_value_str.assign(p, sep - p); + p = sep + 1; + + if (*p) + { + // We have a symbol name + symbol_name = std::move(decode_hex_ascii_string(p)); + if (!symbol_value_str.empty()) + { + nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16); + if (symbol_name == "dispatch_queue_offsets") + m_dispatch_queue_offsets_addr = symbol_value; + } + ++m_qSymbol_index; + } + else + { + // No symbol name, set our symbol index to zero so we can + // read any symbols that we need + m_qSymbol_index = 0; + } + + symbol_name.clear(); + + if (m_qSymbol_index == 0) + { + if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS) + symbol_name = "dispatch_queue_offsets"; + else + ++m_qSymbol_index; + } + +// // Lookup next symbol when we have one... +// if (m_qSymbol_index == 1) +// { +// } + + + if (symbol_name.empty()) + { + // Done with symbol lookups + return SendPacket ("OK"); + } + else + { + std::ostringstream reply; + reply << "qSymbol:"; + for (size_t i = 0; i < symbol_name.size(); ++i) + reply << RAWHEX8(symbol_name[i]); + return SendPacket (reply.str().c_str()); + } +} + // Note that all numeric values returned by qProcessInfo are hex encoded, // including the pid and the cpu type. @@ -5193,6 +5394,23 @@ RNBRemote::HandlePacket_qProcessInfo (const char *p) return SendPacket (rep.str()); } +const RNBRemote::DispatchQueueOffsets * +RNBRemote::GetDispatchQueueOffsets() +{ + if (!m_dispatch_queue_offsets.IsValid() && m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS && m_ctx.HasValidProcessID()) + { + nub_process_t pid = m_ctx.ProcessID(); + nub_size_t bytes_read = DNBProcessMemoryRead(pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets), &m_dispatch_queue_offsets); + if (bytes_read != sizeof(m_dispatch_queue_offsets)) + m_dispatch_queue_offsets.Clear(); + } + + if (m_dispatch_queue_offsets.IsValid()) + return &m_dispatch_queue_offsets; + else + return nullptr; +} + void RNBRemote::EnableCompressionNextSendPacket (compression_types type) { diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index 1f4883ab9e0..3fcccc2e8f3 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -91,7 +91,7 @@ public: query_thread_extra_info, // 'qThreadExtraInfo' query_thread_stop_info, // 'qThreadStopInfo' query_image_offsets, // 'qOffsets' - query_symbol_lookup, // 'gSymbols' + query_symbol_lookup, // 'qSymbol' query_launch_success, // 'qLaunchSuccess' query_register_info, // 'qRegisterInfo' query_shlib_notify_info_addr, // 'qShlibInfoAddr' @@ -195,6 +195,7 @@ public: rnb_err_t HandlePacket_qHostInfo (const char *p); rnb_err_t HandlePacket_qGDBServerVersion (const char *p); rnb_err_t HandlePacket_qProcessInfo (const char *p); + rnb_err_t HandlePacket_qSymbol (const char *p); rnb_err_t HandlePacket_QStartNoAckMode (const char *p); rnb_err_t HandlePacket_QThreadSuffixSupported (const char *p); rnb_err_t HandlePacket_QSetLogging (const char *p); @@ -311,6 +312,68 @@ protected: } }; + + struct DispatchQueueOffsets + { + uint16_t dqo_version; + uint16_t dqo_label; + uint16_t dqo_label_size; + uint16_t dqo_flags; + uint16_t dqo_flags_size; + uint16_t dqo_serialnum; + uint16_t dqo_serialnum_size; + uint16_t dqo_width; + uint16_t dqo_width_size; + uint16_t dqo_running; + uint16_t dqo_running_size; + uint16_t dqo_suspend_cnt; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_suspend_cnt_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_target_queue_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + uint16_t dqo_priority_size; // version 5 and later, starting with Mac OS X 10.10/iOS 8 + + DispatchQueueOffsets () + { + Clear(); + } + + void + Clear() + { + dqo_version = UINT16_MAX; + dqo_label = UINT16_MAX; + dqo_label_size = UINT16_MAX; + dqo_flags = UINT16_MAX; + dqo_flags_size = UINT16_MAX; + dqo_serialnum = UINT16_MAX; + dqo_serialnum_size = UINT16_MAX; + dqo_width = UINT16_MAX; + dqo_width_size = UINT16_MAX; + dqo_running = UINT16_MAX; + dqo_running_size = UINT16_MAX; + dqo_suspend_cnt = UINT16_MAX; + dqo_suspend_cnt_size = UINT16_MAX; + dqo_target_queue = UINT16_MAX; + dqo_target_queue_size = UINT16_MAX; + dqo_priority = UINT16_MAX; + dqo_priority_size = UINT16_MAX; + } + + bool + IsValid () const + { + return dqo_version != UINT16_MAX; + } + + void + GetThreadQueueInfo (nub_process_t pid, + nub_addr_t dispatch_qaddr, + std::string &queue_name, + uint64_t &queue_width, + uint64_t &queue_serialnum) const; + }; + rnb_err_t GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait); rnb_err_t SendPacket (const std::string &); std::string CompressString (const std::string &); @@ -327,12 +390,18 @@ protected: compression_types GetCompressionType (); + const DispatchQueueOffsets * + GetDispatchQueueOffsets(); + RNBContext m_ctx; // process context RNBSocket m_comm; // communication port std::string m_arch; nub_thread_t m_continue_thread; // thread to continue; 0 for any, -1 for all nub_thread_t m_thread; // thread for other ops; 0 for any, -1 for all PThreadMutex m_mutex; // Mutex that protects + DispatchQueueOffsets m_dispatch_queue_offsets; + nub_addr_t m_dispatch_queue_offsets_addr; + uint32_t m_qSymbol_index; uint32_t m_packets_recvd; Packet::collection m_packets; std::deque<std::string> m_rx_packets; |