diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp')
-rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp | 7509 |
1 files changed, 3254 insertions, 4255 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index a792bbbd1385..f9bbaef60215 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// - #include "GDBRemoteCommunicationClient.h" // C Includes @@ -15,48 +14,48 @@ #include <sys/stat.h> // C++ Includes -#include <sstream> #include <numeric> +#include <sstream> // Other libraries and framework includes -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/Triple.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" -#include "lldb/Host/ConnectionFileDescriptor.h" -#include "lldb/Host/Endian.h" -#include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" -#include "lldb/Host/TimeValue.h" +#include "lldb/Interpreter/Args.h" #include "lldb/Symbol/Symbol.h" -#include "lldb/Target/Target.h" #include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/LLDBAssert.h" // Project includes -#include "Utility/StringExtractorGDBRemote.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" +#include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Config.h" -#if defined (HAVE_LIBCOMPRESSION) +#include "llvm/ADT/StringSwitch.h" + +#if defined(HAVE_LIBCOMPRESSION) #include <compression.h> #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; +using namespace std::chrono; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() - : GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"), + : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"), m_supports_not_sending_acks(eLazyBoolCalculate), m_supports_thread_suffix(eLazyBoolCalculate), m_supports_threads_in_stop_reply(eLazyBoolCalculate), @@ -77,8 +76,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply(eLazyBoolCalculate), - m_supports_p(eLazyBoolCalculate), - m_supports_x(eLazyBoolCalculate), + m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate), m_avoid_g_packets(eLazyBoolCalculate), m_supports_QSaveRegisterState(eLazyBoolCalculate), m_supports_qXfer_auxv_read(eLazyBoolCalculate), @@ -88,503 +86,449 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), - m_supports_qProcessInfoPID(true), - m_supports_qfProcessInfo(true), - m_supports_qUserName(true), - m_supports_qGroupName(true), - m_supports_qThreadStopInfo(true), - m_supports_z0(true), - m_supports_z1(true), - m_supports_z2(true), - m_supports_z3(true), - m_supports_z4(true), - m_supports_QEnvironment(true), - m_supports_QEnvironmentHexEncoded(true), - m_supports_qSymbol(true), - m_qSymbol_requests_done(false), - m_supports_qModuleInfo(true), - m_supports_jThreadsInfo(true), - m_curr_pid(LLDB_INVALID_PROCESS_ID), - m_curr_tid(LLDB_INVALID_THREAD_ID), + m_supports_jGetSharedCacheInfo(eLazyBoolCalculate), + m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), + m_supports_qUserName(true), m_supports_qGroupName(true), + m_supports_qThreadStopInfo(true), m_supports_z0(true), + m_supports_z1(true), m_supports_z2(true), m_supports_z3(true), + m_supports_z4(true), m_supports_QEnvironment(true), + m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), + m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), + m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), + m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID), m_curr_tid_run(LLDB_INVALID_THREAD_ID), - m_num_supported_hardware_watchpoints(0), - m_async_mutex(), - m_async_packet_predicate(false), - m_async_packet(), - m_async_result(PacketResult::Success), - m_async_response(), - m_async_signal(-1), - m_interrupt_sent(false), - m_thread_id_to_used_usec_map(), - m_host_arch(), - m_process_arch(), - m_os_version_major(UINT32_MAX), - m_os_version_minor(UINT32_MAX), - m_os_version_update(UINT32_MAX), - m_os_build(), - m_os_kernel(), - m_hostname(), - m_gdb_server_name(), - m_gdb_server_version(UINT32_MAX), - m_default_packet_timeout(0), - m_max_packet_size(0) -{ -} + m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(), + m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX), + m_os_version_update(UINT32_MAX), m_os_build(), m_os_kernel(), + m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX), + m_default_packet_timeout(0), m_max_packet_size(0), + m_qSupported_response(), m_supported_async_json_packets_is_valid(false), + m_supported_async_json_packets_sp() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- -GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() -{ - if (IsConnected()) - Disconnect(); -} - -bool -GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) -{ - ResetDiscoverableSettings(false); - - // Start the read thread after we send the handshake ack since if we - // fail to send the handshake ack, there is no reason to continue... - if (SendAck()) - { - // Wait for any responses that might have been queued up in the remote - // GDB server and flush them all - StringExtractorGDBRemote response; - PacketResult packet_result = PacketResult::Success; - const uint32_t timeout_usec = 10 * 1000; // Wait for 10 ms for a response - while (packet_result == PacketResult::Success) - packet_result = ReadPacket (response, timeout_usec, false); - - // The return value from QueryNoAckModeSupported() is true if the packet - // was sent and _any_ response (including UNIMPLEMENTED) was received), - // or false if no response was received. This quickly tells us if we have - // a live connection to a remote GDB server... - if (QueryNoAckModeSupported()) - { - return true; - } - else - { - if (error_ptr) - error_ptr->SetErrorString("failed to get reply to handshake packet"); - } - } - else - { - if (error_ptr) - error_ptr->SetErrorString("failed to send the handshake ack"); - } - return false; +GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { + if (IsConnected()) + Disconnect(); } -bool -GDBRemoteCommunicationClient::GetEchoSupported () -{ - if (m_supports_qEcho == eLazyBoolCalculate) - { - GetRemoteQSupported(); - } - return m_supports_qEcho == eLazyBoolYes; -} +bool GDBRemoteCommunicationClient::HandshakeWithServer(Error *error_ptr) { + ResetDiscoverableSettings(false); + // Start the read thread after we send the handshake ack since if we + // fail to send the handshake ack, there is no reason to continue... + if (SendAck()) { + // Wait for any responses that might have been queued up in the remote + // GDB server and flush them all + StringExtractorGDBRemote response; + PacketResult packet_result = PacketResult::Success; + while (packet_result == PacketResult::Success) + packet_result = ReadPacket(response, milliseconds(10), false); -bool -GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported () -{ - if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) - { - GetRemoteQSupported(); + // The return value from QueryNoAckModeSupported() is true if the packet + // was sent and _any_ response (including UNIMPLEMENTED) was received), + // or false if no response was received. This quickly tells us if we have + // a live connection to a remote GDB server... + if (QueryNoAckModeSupported()) { + return true; + } else { + if (error_ptr) + error_ptr->SetErrorString("failed to get reply to handshake packet"); } - return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; + } else { + if (error_ptr) + error_ptr->SetErrorString("failed to send the handshake ack"); + } + return false; } -bool -GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported () -{ - if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) - { - GetRemoteQSupported(); - } - return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; +bool GDBRemoteCommunicationClient::GetEchoSupported() { + if (m_supports_qEcho == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qEcho == eLazyBoolYes; } -bool -GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported () -{ - if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) - { - GetRemoteQSupported(); - } - return m_supports_qXfer_libraries_read == eLazyBoolYes; +bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { + if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; } -bool -GDBRemoteCommunicationClient::GetQXferAuxvReadSupported () -{ - if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) - { - GetRemoteQSupported(); - } - return m_supports_qXfer_auxv_read == eLazyBoolYes; +bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() { + if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; } -bool -GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported () -{ - if (m_supports_qXfer_features_read == eLazyBoolCalculate) - { - GetRemoteQSupported(); - } - return m_supports_qXfer_features_read == eLazyBoolYes; +bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() { + if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_libraries_read == eLazyBoolYes; } -uint64_t -GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() -{ - if (m_max_packet_size == 0) - { - GetRemoteQSupported(); - } - return m_max_packet_size; +bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() { + if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_auxv_read == eLazyBoolYes; } -bool -GDBRemoteCommunicationClient::QueryNoAckModeSupported () -{ - if (m_supports_not_sending_acks == eLazyBoolCalculate) - { - m_send_acks = true; - m_supports_not_sending_acks = eLazyBoolNo; - - // This is the first real packet that we'll send in a debug session and it may take a little - // longer than normal to receive a reply. Wait at least 6 seconds for a reply to this packet. - - const uint32_t minimum_timeout = 6; - uint32_t old_timeout = GetPacketTimeoutInMicroSeconds() / lldb_private::TimeValue::MicroSecPerSec; - GDBRemoteCommunication::ScopedTimeout timeout (*this, std::max (old_timeout, minimum_timeout)); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - m_send_acks = false; - m_supports_not_sending_acks = eLazyBoolYes; - } - return true; - } - } - return false; +bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { + if (m_supports_qXfer_features_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_features_read == eLazyBoolYes; } -void -GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported () -{ - if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) - { - m_supports_threads_in_stop_reply = eLazyBoolNo; - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - m_supports_threads_in_stop_reply = eLazyBoolYes; - } - } +uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { + if (m_max_packet_size == 0) { + GetRemoteQSupported(); + } + return m_max_packet_size; } -bool -GDBRemoteCommunicationClient::GetVAttachOrWaitSupported () -{ - if (m_attach_or_wait_reply == eLazyBoolCalculate) - { - m_attach_or_wait_reply = eLazyBoolNo; - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - m_attach_or_wait_reply = eLazyBoolYes; - } - } - if (m_attach_or_wait_reply == eLazyBoolYes) - return true; - else - return false; -} +bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { + if (m_supports_not_sending_acks == eLazyBoolCalculate) { + m_send_acks = true; + m_supports_not_sending_acks = eLazyBoolNo; -bool -GDBRemoteCommunicationClient::GetSyncThreadStateSupported () -{ - if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) - { - m_prepare_for_reg_writing_reply = eLazyBoolNo; - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - m_prepare_for_reg_writing_reply = eLazyBoolYes; - } + // This is the first real packet that we'll send in a debug session and it + // may take a little + // longer than normal to receive a reply. Wait at least 6 seconds for a + // reply to this packet. + + ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6))); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_send_acks = false; + m_supports_not_sending_acks = eLazyBoolYes; + } + return true; } - if (m_prepare_for_reg_writing_reply == eLazyBoolYes) - return true; - else - return false; + } + return false; } +void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() { + if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { + m_supports_threads_in_stop_reply = eLazyBoolNo; -void -GDBRemoteCommunicationClient::ResetDiscoverableSettings (bool did_exec) -{ - if (did_exec == false) - { - // Hard reset everything, this is when we first connect to a GDB server - m_supports_not_sending_acks = eLazyBoolCalculate; - m_supports_thread_suffix = eLazyBoolCalculate; - m_supports_threads_in_stop_reply = eLazyBoolCalculate; - m_supports_vCont_c = eLazyBoolCalculate; - m_supports_vCont_C = eLazyBoolCalculate; - m_supports_vCont_s = eLazyBoolCalculate; - m_supports_vCont_S = eLazyBoolCalculate; - m_supports_p = eLazyBoolCalculate; - m_supports_x = eLazyBoolCalculate; - m_supports_QSaveRegisterState = eLazyBoolCalculate; - m_qHostInfo_is_valid = eLazyBoolCalculate; - m_curr_pid_is_valid = eLazyBoolCalculate; - m_qGDBServerVersion_is_valid = eLazyBoolCalculate; - m_supports_alloc_dealloc_memory = eLazyBoolCalculate; - m_supports_memory_region_info = eLazyBoolCalculate; - m_prepare_for_reg_writing_reply = eLazyBoolCalculate; - m_attach_or_wait_reply = eLazyBoolCalculate; - m_avoid_g_packets = eLazyBoolCalculate; - m_supports_qXfer_auxv_read = eLazyBoolCalculate; - m_supports_qXfer_libraries_read = eLazyBoolCalculate; - m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; - m_supports_qXfer_features_read = eLazyBoolCalculate; - m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; - m_supports_qProcessInfoPID = true; - m_supports_qfProcessInfo = true; - m_supports_qUserName = true; - m_supports_qGroupName = true; - m_supports_qThreadStopInfo = true; - m_supports_z0 = true; - m_supports_z1 = true; - m_supports_z2 = true; - m_supports_z3 = true; - m_supports_z4 = true; - m_supports_QEnvironment = true; - m_supports_QEnvironmentHexEncoded = true; - m_supports_qSymbol = true; - m_qSymbol_requests_done = false; - m_supports_qModuleInfo = true; - m_host_arch.Clear(); - m_os_version_major = UINT32_MAX; - m_os_version_minor = UINT32_MAX; - m_os_version_update = UINT32_MAX; - m_os_build.clear(); - m_os_kernel.clear(); - m_hostname.clear(); - m_gdb_server_name.clear(); - m_gdb_server_version = UINT32_MAX; - m_default_packet_timeout = 0; - m_max_packet_size = 0; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, + false) == PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_threads_in_stop_reply = eLazyBoolYes; } - - // These flags should be reset when we first connect to a GDB server - // and when our inferior process execs - m_qProcessInfo_is_valid = eLazyBoolCalculate; - m_process_arch.Clear(); + } } -void -GDBRemoteCommunicationClient::GetRemoteQSupported () -{ - // Clear out any capabilities we expect to see in the qSupported response - m_supports_qXfer_auxv_read = eLazyBoolNo; - m_supports_qXfer_libraries_read = eLazyBoolNo; - m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; - m_supports_augmented_libraries_svr4_read = eLazyBoolNo; - m_supports_qXfer_features_read = eLazyBoolNo; - m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if not, we assume no limit - - // build the qSupported packet - std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"}; - StreamString packet; - packet.PutCString( "qSupported" ); - for ( uint32_t i = 0; i < features.size( ); ++i ) - { - packet.PutCString( i==0 ? ":" : ";"); - packet.PutCString( features[i].c_str( ) ); - } +bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() { + if (m_attach_or_wait_reply == eLazyBoolCalculate) { + m_attach_or_wait_reply = eLazyBoolNo; StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet.GetData(), - response, - /*send_async=*/false) == PacketResult::Success) - { - const char *response_cstr = response.GetStringRef().c_str(); - if (::strstr (response_cstr, "qXfer:auxv:read+")) - m_supports_qXfer_auxv_read = eLazyBoolYes; - if (::strstr (response_cstr, "qXfer:libraries-svr4:read+")) - m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; - if (::strstr (response_cstr, "augmented-libraries-svr4-read")) - { - m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied - m_supports_augmented_libraries_svr4_read = eLazyBoolYes; - } - if (::strstr (response_cstr, "qXfer:libraries:read+")) - m_supports_qXfer_libraries_read = eLazyBoolYes; - if (::strstr (response_cstr, "qXfer:features:read+")) - m_supports_qXfer_features_read = eLazyBoolYes; - - - // Look for a list of compressions in the features list e.g. - // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma - const char *features_list = ::strstr (response_cstr, "qXfer:features:"); - if (features_list) - { - const char *compressions = ::strstr (features_list, "SupportedCompressions="); - if (compressions) - { - std::vector<std::string> supported_compressions; - compressions += sizeof ("SupportedCompressions=") - 1; - const char *end_of_compressions = strchr (compressions, ';'); - if (end_of_compressions == NULL) - { - end_of_compressions = strchr (compressions, '\0'); - } - const char *current_compression = compressions; - while (current_compression < end_of_compressions) - { - const char *next_compression_name = strchr (current_compression, ','); - const char *end_of_this_word = next_compression_name; - if (next_compression_name == NULL || end_of_compressions < next_compression_name) - { - end_of_this_word = end_of_compressions; - } - - if (end_of_this_word) - { - if (end_of_this_word == current_compression) - { - current_compression++; - } - else - { - std::string this_compression (current_compression, end_of_this_word - current_compression); - supported_compressions.push_back (this_compression); - current_compression = end_of_this_word + 1; - } - } - else - { - supported_compressions.push_back (current_compression); - current_compression = end_of_compressions; - } - } + if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, + false) == PacketResult::Success) { + if (response.IsOKResponse()) + m_attach_or_wait_reply = eLazyBoolYes; + } + } + if (m_attach_or_wait_reply == eLazyBoolYes) + return true; + else + return false; +} - if (supported_compressions.size() > 0) - { - MaybeEnableCompression (supported_compressions); - } - } - } +bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() { + if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { + m_prepare_for_reg_writing_reply = eLazyBoolNo; - if (::strstr (response_cstr, "qEcho")) - m_supports_qEcho = eLazyBoolYes; - else - m_supports_qEcho = eLazyBoolNo; - - const char *packet_size_str = ::strstr (response_cstr, "PacketSize="); - if (packet_size_str) - { - StringExtractorGDBRemote packet_response(packet_size_str + strlen("PacketSize=")); - m_max_packet_size = packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); - if (m_max_packet_size == 0) - { - m_max_packet_size = UINT64_MAX; // Must have been a garbled response - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); - if (log) - log->Printf ("Garbled PacketSize spec in qSupported response"); - } - } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, + false) == PacketResult::Success) { + if (response.IsOKResponse()) + m_prepare_for_reg_writing_reply = eLazyBoolYes; } + } + if (m_prepare_for_reg_writing_reply == eLazyBoolYes) + return true; + else + return false; } -bool -GDBRemoteCommunicationClient::GetThreadSuffixSupported () -{ - if (m_supports_thread_suffix == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_thread_suffix = eLazyBoolNo; - if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - m_supports_thread_suffix = eLazyBoolYes; - } +void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { + if (did_exec == false) { + // Hard reset everything, this is when we first connect to a GDB server + m_supports_not_sending_acks = eLazyBoolCalculate; + m_supports_thread_suffix = eLazyBoolCalculate; + m_supports_threads_in_stop_reply = eLazyBoolCalculate; + m_supports_vCont_c = eLazyBoolCalculate; + m_supports_vCont_C = eLazyBoolCalculate; + m_supports_vCont_s = eLazyBoolCalculate; + m_supports_vCont_S = eLazyBoolCalculate; + m_supports_p = eLazyBoolCalculate; + m_supports_x = eLazyBoolCalculate; + m_supports_QSaveRegisterState = eLazyBoolCalculate; + m_qHostInfo_is_valid = eLazyBoolCalculate; + m_curr_pid_is_valid = eLazyBoolCalculate; + m_qGDBServerVersion_is_valid = eLazyBoolCalculate; + m_supports_alloc_dealloc_memory = eLazyBoolCalculate; + m_supports_memory_region_info = eLazyBoolCalculate; + m_prepare_for_reg_writing_reply = eLazyBoolCalculate; + m_attach_or_wait_reply = eLazyBoolCalculate; + m_avoid_g_packets = eLazyBoolCalculate; + m_supports_qXfer_auxv_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qXfer_features_read = eLazyBoolCalculate; + m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qProcessInfoPID = true; + m_supports_qfProcessInfo = true; + m_supports_qUserName = true; + m_supports_qGroupName = true; + m_supports_qThreadStopInfo = true; + m_supports_z0 = true; + m_supports_z1 = true; + m_supports_z2 = true; + m_supports_z3 = true; + m_supports_z4 = true; + m_supports_QEnvironment = true; + m_supports_QEnvironmentHexEncoded = true; + m_supports_qSymbol = true; + m_qSymbol_requests_done = false; + m_supports_qModuleInfo = true; + m_host_arch.Clear(); + m_os_version_major = UINT32_MAX; + m_os_version_minor = UINT32_MAX; + m_os_version_update = UINT32_MAX; + m_os_build.clear(); + m_os_kernel.clear(); + m_hostname.clear(); + m_gdb_server_name.clear(); + m_gdb_server_version = UINT32_MAX; + m_default_packet_timeout = seconds(0); + m_max_packet_size = 0; + m_qSupported_response.clear(); + m_supported_async_json_packets_is_valid = false; + m_supported_async_json_packets_sp.reset(); + m_supports_jModulesInfo = true; + } + + // These flags should be reset when we first connect to a GDB server + // and when our inferior process execs + m_qProcessInfo_is_valid = eLazyBoolCalculate; + m_process_arch.Clear(); +} + +void GDBRemoteCommunicationClient::GetRemoteQSupported() { + // Clear out any capabilities we expect to see in the qSupported response + m_supports_qXfer_auxv_read = eLazyBoolNo; + m_supports_qXfer_libraries_read = eLazyBoolNo; + m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; + m_supports_augmented_libraries_svr4_read = eLazyBoolNo; + m_supports_qXfer_features_read = eLazyBoolNo; + m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if + // not, we assume no limit + + // build the qSupported packet + std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"}; + StreamString packet; + packet.PutCString("qSupported"); + for (uint32_t i = 0; i < features.size(); ++i) { + packet.PutCString(i == 0 ? ":" : ";"); + packet.PutCString(features[i]); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, + /*send_async=*/false) == + PacketResult::Success) { + const char *response_cstr = response.GetStringRef().c_str(); + + // Hang on to the qSupported packet, so that platforms can do custom + // configuration of the transport before attaching/launching the + // process. + m_qSupported_response = response_cstr; + + if (::strstr(response_cstr, "qXfer:auxv:read+")) + m_supports_qXfer_auxv_read = eLazyBoolYes; + if (::strstr(response_cstr, "qXfer:libraries-svr4:read+")) + m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; + if (::strstr(response_cstr, "augmented-libraries-svr4-read")) { + m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied + m_supports_augmented_libraries_svr4_read = eLazyBoolYes; + } + if (::strstr(response_cstr, "qXfer:libraries:read+")) + m_supports_qXfer_libraries_read = eLazyBoolYes; + if (::strstr(response_cstr, "qXfer:features:read+")) + m_supports_qXfer_features_read = eLazyBoolYes; + + // Look for a list of compressions in the features list e.g. + // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma + const char *features_list = ::strstr(response_cstr, "qXfer:features:"); + if (features_list) { + const char *compressions = + ::strstr(features_list, "SupportedCompressions="); + if (compressions) { + std::vector<std::string> supported_compressions; + compressions += sizeof("SupportedCompressions=") - 1; + const char *end_of_compressions = strchr(compressions, ';'); + if (end_of_compressions == NULL) { + end_of_compressions = strchr(compressions, '\0'); + } + const char *current_compression = compressions; + while (current_compression < end_of_compressions) { + const char *next_compression_name = strchr(current_compression, ','); + const char *end_of_this_word = next_compression_name; + if (next_compression_name == NULL || + end_of_compressions < next_compression_name) { + end_of_this_word = end_of_compressions; + } + + if (end_of_this_word) { + if (end_of_this_word == current_compression) { + current_compression++; + } else { + std::string this_compression( + current_compression, end_of_this_word - current_compression); + supported_compressions.push_back(this_compression); + current_compression = end_of_this_word + 1; + } + } else { + supported_compressions.push_back(current_compression); + current_compression = end_of_compressions; + } + } + + if (supported_compressions.size() > 0) { + MaybeEnableCompression(supported_compressions); + } + } + } + + if (::strstr(response_cstr, "qEcho")) + m_supports_qEcho = eLazyBoolYes; + else + m_supports_qEcho = eLazyBoolNo; + + const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); + if (packet_size_str) { + StringExtractorGDBRemote packet_response(packet_size_str + + strlen("PacketSize=")); + m_max_packet_size = + packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); + if (m_max_packet_size == 0) { + m_max_packet_size = UINT64_MAX; // Must have been a garbled response + Log *log( + ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf("Garbled PacketSize spec in qSupported response"); + } } - return m_supports_thread_suffix; + } } -bool -GDBRemoteCommunicationClient::GetVContSupported (char flavor) -{ - if (m_supports_vCont_c == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_vCont_any = eLazyBoolNo; - m_supports_vCont_all = eLazyBoolNo; - m_supports_vCont_c = eLazyBoolNo; - m_supports_vCont_C = eLazyBoolNo; - m_supports_vCont_s = eLazyBoolNo; - m_supports_vCont_S = eLazyBoolNo; - if (SendPacketAndWaitForResponse("vCont?", response, false) == PacketResult::Success) - { - const char *response_cstr = response.GetStringRef().c_str(); - if (::strstr (response_cstr, ";c")) - m_supports_vCont_c = eLazyBoolYes; - - if (::strstr (response_cstr, ";C")) - m_supports_vCont_C = eLazyBoolYes; - - if (::strstr (response_cstr, ";s")) - m_supports_vCont_s = eLazyBoolYes; - - if (::strstr (response_cstr, ";S")) - m_supports_vCont_S = eLazyBoolYes; - - if (m_supports_vCont_c == eLazyBoolYes && - m_supports_vCont_C == eLazyBoolYes && - m_supports_vCont_s == eLazyBoolYes && - m_supports_vCont_S == eLazyBoolYes) - { - m_supports_vCont_all = eLazyBoolYes; - } - - if (m_supports_vCont_c == eLazyBoolYes || - m_supports_vCont_C == eLazyBoolYes || - m_supports_vCont_s == eLazyBoolYes || - m_supports_vCont_S == eLazyBoolYes) - { - m_supports_vCont_any = eLazyBoolYes; - } - } - } - - switch (flavor) - { - case 'a': return m_supports_vCont_any; - case 'A': return m_supports_vCont_all; - case 'c': return m_supports_vCont_c; - case 'C': return m_supports_vCont_C; - case 's': return m_supports_vCont_s; - case 'S': return m_supports_vCont_S; - default: break; + +bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() { + if (m_supports_thread_suffix == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_thread_suffix = eLazyBoolNo; + if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, + false) == PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_thread_suffix = eLazyBoolYes; } - return false; + } + return m_supports_thread_suffix; +} +bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) { + if (m_supports_vCont_c == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_vCont_any = eLazyBoolNo; + m_supports_vCont_all = eLazyBoolNo; + m_supports_vCont_c = eLazyBoolNo; + m_supports_vCont_C = eLazyBoolNo; + m_supports_vCont_s = eLazyBoolNo; + m_supports_vCont_S = eLazyBoolNo; + if (SendPacketAndWaitForResponse("vCont?", response, false) == + PacketResult::Success) { + const char *response_cstr = response.GetStringRef().c_str(); + if (::strstr(response_cstr, ";c")) + m_supports_vCont_c = eLazyBoolYes; + + if (::strstr(response_cstr, ";C")) + m_supports_vCont_C = eLazyBoolYes; + + if (::strstr(response_cstr, ";s")) + m_supports_vCont_s = eLazyBoolYes; + + if (::strstr(response_cstr, ";S")) + m_supports_vCont_S = eLazyBoolYes; + + if (m_supports_vCont_c == eLazyBoolYes && + m_supports_vCont_C == eLazyBoolYes && + m_supports_vCont_s == eLazyBoolYes && + m_supports_vCont_S == eLazyBoolYes) { + m_supports_vCont_all = eLazyBoolYes; + } + + if (m_supports_vCont_c == eLazyBoolYes || + m_supports_vCont_C == eLazyBoolYes || + m_supports_vCont_s == eLazyBoolYes || + m_supports_vCont_S == eLazyBoolYes) { + m_supports_vCont_any = eLazyBoolYes; + } + } + } + + switch (flavor) { + case 'a': + return m_supports_vCont_any; + case 'A': + return m_supports_vCont_all; + case 'c': + return m_supports_vCont_c; + case 'C': + return m_supports_vCont_C; + case 's': + return m_supports_vCont_s; + case 'S': + return m_supports_vCont_S; + default: + break; + } + return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( + lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response, + bool send_async) { + Lock lock(*this, send_async); + if (!lock) { + if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( + GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) + log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex " + "for %s packet.", + __FUNCTION__, payload.GetData()); + return PacketResult::ErrorNoSequenceLock; + } + + if (GetThreadSuffixSupported()) + payload.Printf(";thread:%4.4" PRIx64 ";", tid); + else { + if (!SetCurrentThread(tid)) + return PacketResult::ErrorSendFailed; + } + + return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); } // Check if the target supports 'p' packet. It sends out a 'p' @@ -592,4043 +536,3098 @@ GDBRemoteCommunicationClient::GetVContSupported (char flavor) // that support is available. // // Takes a valid thread ID because p needs to apply to a thread. -bool -GDBRemoteCommunicationClient::GetpPacketSupported (lldb::tid_t tid) -{ - if (m_supports_p == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_p = eLazyBoolNo; - char packet[256]; - if (GetThreadSuffixSupported()) - snprintf(packet, sizeof(packet), "p0;thread:%" PRIx64 ";", tid); - else - snprintf(packet, sizeof(packet), "p0"); - - if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - m_supports_p = eLazyBoolYes; - } - } - 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; - response.SetResponseValidatorToJSON(); - if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - { - m_supports_jThreadsInfo = false; - } - else if (!response.Empty()) - { - object_sp = StructuredData::ParseJSON (response.GetStringRef()); - } - } +bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { + if (m_supports_p == eLazyBoolCalculate) { + m_supports_p = eLazyBoolNo; + StreamString payload; + payload.PutCString("p0"); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), + response, false) == + PacketResult::Success && + response.IsNormalResponse()) { + m_supports_p = eLazyBoolYes; } - return object_sp; + } + return m_supports_p; } +StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { + // Get information on all threads at one using the "jThreadsInfo" packet + StructuredData::ObjectSP object_sp; -bool -GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported () -{ - if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_jThreadExtendedInfo = eLazyBoolNo; - if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - m_supports_jThreadExtendedInfo = eLazyBoolYes; - } - } + if (m_supports_jThreadsInfo) { + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + 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 m_supports_jThreadExtendedInfo; + } + return object_sp; } -bool -GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported () -{ - if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; - if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; - } - } +bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { + if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jThreadExtendedInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jThreadExtendedInfo = eLazyBoolYes; + } } - return m_supports_jLoadedDynamicLibrariesInfos; + } + return m_supports_jThreadExtendedInfo; } -bool -GDBRemoteCommunicationClient::GetxPacketSupported () -{ - if (m_supports_x == eLazyBoolCalculate) - { - StringExtractorGDBRemote response; - m_supports_x = eLazyBoolNo; - char packet[256]; - snprintf (packet, sizeof (packet), "x0,0"); - if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - m_supports_x = eLazyBoolYes; - } +bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() { + if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", + response, + false) == PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; + } } - return m_supports_x; + } + return m_supports_jLoadedDynamicLibrariesInfos; } -GDBRemoteCommunicationClient::PacketResult -GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses -( - const char *payload_prefix, - std::string &response_string -) -{ - Mutex::Locker locker; - if (!GetSequenceMutex(locker, - "ProcessGDBRemote::SendPacketsAndConcatenateResponses() failed due to not getting the sequence mutex")) - { - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); - if (log) - log->Printf("error: failed to get packet sequence mutex, not sending packets with prefix '%s'", - payload_prefix); - return PacketResult::ErrorNoSequenceLock; - } - - response_string = ""; - std::string payload_prefix_str(payload_prefix); - unsigned int response_size = 0x1000; - if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet - response_size = GetRemoteMaxPacketSize(); - } - - for (unsigned int offset = 0; true; offset += response_size) - { - StringExtractorGDBRemote this_response; - // Construct payload - char sizeDescriptor[128]; - snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, response_size); - PacketResult result = SendPacketAndWaitForResponse((payload_prefix_str + sizeDescriptor).c_str(), - this_response, - /*send_async=*/false); - if (result != PacketResult::Success) - return result; - - const std::string &this_string = this_response.GetStringRef(); - - // Check for m or l as first character; l seems to mean this is the last chunk - char first_char = *this_string.c_str(); - if (first_char != 'm' && first_char != 'l') - { - return PacketResult::ErrorReplyInvalid; - } - // Concatenate the result so far (skipping 'm' or 'l') - response_string.append(this_string, 1, std::string::npos); - if (first_char == 'l') - // We're done - return PacketResult::Success; +bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() { + if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jGetSharedCacheInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jGetSharedCacheInfo = eLazyBoolYes; + } } + } + return m_supports_jGetSharedCacheInfo; } -GDBRemoteCommunicationClient::PacketResult -GDBRemoteCommunicationClient::SendPacketAndWaitForResponse -( - const char *payload, - StringExtractorGDBRemote &response, - bool send_async -) -{ - return SendPacketAndWaitForResponse (payload, - ::strlen (payload), - response, - send_async); -} - -GDBRemoteCommunicationClient::PacketResult -GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *payload, - size_t payload_length, - StringExtractorGDBRemote &response) -{ - PacketResult packet_result = SendPacketNoLock(payload, payload_length); - if (packet_result == PacketResult::Success) - { - const size_t max_response_retries = 3; - for (size_t i=0; i<max_response_retries; ++i) - { - packet_result = ReadPacket(response, GetPacketTimeoutInMicroSeconds (), true); - // Make sure we received a response - if (packet_result != PacketResult::Success) - return packet_result; - // Make sure our response is valid for the payload that was sent - if (response.ValidateResponse()) - return packet_result; - // Response says it wasn't valid - Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS); - if (log) - log->Printf("error: packet with payload \"%*s\" got invalid response \"%s\": %s", - (int)payload_length, - payload, - response.GetStringRef().c_str(), - (i == (max_response_retries - 1)) ? "using invalid response and giving up" : "ignoring response and waiting for another"); - } +bool GDBRemoteCommunicationClient::GetxPacketSupported() { + if (m_supports_x == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_x = eLazyBoolNo; + char packet[256]; + snprintf(packet, sizeof(packet), "x0,0"); + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_x = eLazyBoolYes; } - return packet_result; + } + return m_supports_x; } GDBRemoteCommunicationClient::PacketResult -GDBRemoteCommunicationClient::SendPacketAndWaitForResponse -( - const char *payload, - size_t payload_length, - StringExtractorGDBRemote &response, - bool send_async -) -{ - PacketResult packet_result = PacketResult::ErrorSendFailed; - Mutex::Locker locker; - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); - - // In order to stop async notifications from being processed in the middle of the - // send/receive sequence Hijack the broadcast. Then rebroadcast any events when we are done. - static ListenerSP hijack_listener_sp(Listener::MakeListener("lldb.NotifyHijacker")); - HijackBroadcaster(hijack_listener_sp, eBroadcastBitGdbReadThreadGotNotify); - - if (GetSequenceMutex (locker)) - { - packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response); - } - else - { - if (send_async) - { - if (IsRunning()) - { - std::lock_guard<std::recursive_mutex> guard(m_async_mutex); - m_async_packet.assign(payload, payload_length); - m_async_response.CopyResponseValidator(response); - m_async_packet_predicate.SetValue (true, eBroadcastNever); - - if (log) - log->Printf ("async: async packet = %s", m_async_packet.c_str()); - - bool timed_out = false; - if (SendInterrupt(locker, 2, timed_out)) - { - if (m_interrupt_sent) - { - m_interrupt_sent = false; - TimeValue timeout_time; - timeout_time = TimeValue::Now(); - timeout_time.OffsetWithSeconds (m_packet_timeout); - - if (log) - log->Printf ("async: sent interrupt"); - - if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out)) - { - if (log) - log->Printf ("async: got response"); - - // Swap the response buffer to avoid malloc and string copy - response.GetStringRef().swap (m_async_response.GetStringRef()); - packet_result = m_async_result; - } - else - { - if (log) - log->Printf ("async: timed out waiting for response"); - } - - // Make sure we wait until the continue packet has been sent again... - if (m_private_is_running.WaitForValueEqualTo (true, &timeout_time, &timed_out)) - { - if (log) - { - if (timed_out) - log->Printf ("async: timed out waiting for process to resume, but process was resumed"); - else - log->Printf ("async: async packet sent"); - } - } - else - { - if (log) - log->Printf ("async: timed out waiting for process to resume"); - } - } - else - { - // We had a racy condition where we went to send the interrupt - // yet we were able to get the lock, so the process must have - // just stopped? - if (log) - log->Printf ("async: got lock without sending interrupt"); - // Send the packet normally since we got the lock - packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response); - } - } - else - { - if (log) - log->Printf ("async: failed to interrupt"); - } - - m_async_response.SetResponseValidator(nullptr, nullptr); - - } - else - { - if (log) - log->Printf ("async: not running, async is ignored"); - } - } - else - { - if (log) - log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload); - } - } - - // Remove our Hijacking listener from the broadcast. - RestoreBroadcaster(); - - // If a notification event occurred, rebroadcast since it can now be processed safely. - EventSP event_sp; - if (hijack_listener_sp->GetNextEvent(event_sp)) - BroadcastEvent(event_sp); - - return packet_result; -} - -static const char *end_delimiter = "--end--;"; -static const int end_delimiter_len = 8; - -std::string -GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData -( ProcessGDBRemote *process, - StringExtractorGDBRemote& profileDataExtractor -) -{ - std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map; - std::stringstream final_output; - std::string name, value; - - // Going to assuming thread_used_usec comes first, else bail out. - while (profileDataExtractor.GetNameColonValue(name, value)) - { - if (name.compare("thread_used_id") == 0) - { - StringExtractor threadIDHexExtractor(value.c_str()); - uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); - - bool has_used_usec = false; - uint32_t curr_used_usec = 0; - std::string usec_name, usec_value; - uint32_t input_file_pos = profileDataExtractor.GetFilePos(); - if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) - { - if (usec_name.compare("thread_used_usec") == 0) - { - has_used_usec = true; - curr_used_usec = strtoull(usec_value.c_str(), NULL, 0); - } - else - { - // We didn't find what we want, it is probably - // an older version. Bail out. - profileDataExtractor.SetFilePos(input_file_pos); - } - } - - if (has_used_usec) - { - uint32_t prev_used_usec = 0; - std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); - if (iterator != m_thread_id_to_used_usec_map.end()) - { - prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; - } - - uint32_t real_used_usec = curr_used_usec - prev_used_usec; - // A good first time record is one that runs for at least 0.25 sec - bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); - bool good_subsequent_time = (prev_used_usec > 0) && - ((real_used_usec > 0) || (process->HasAssignedIndexIDToThread(thread_id))); - - if (good_first_time || good_subsequent_time) - { - // We try to avoid doing too many index id reservation, - // resulting in fast increase of index ids. - - final_output << name << ":"; - int32_t index_id = process->AssignIndexIDToThread(thread_id); - final_output << index_id << ";"; - - final_output << usec_name << ":" << usec_value << ";"; - } - else - { - // Skip past 'thread_used_name'. - std::string local_name, local_value; - profileDataExtractor.GetNameColonValue(local_name, local_value); - } - - // Store current time as previous time so that they can be compared later. - new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; - } - else - { - // Bail out and use old string. - final_output << name << ":" << value << ";"; - } - } - else - { - final_output << name << ":" << value << ";"; - } - } - final_output << end_delimiter; - m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; - - return final_output.str(); -} - -bool -GDBRemoteCommunicationClient::SendvContPacket -( - ProcessGDBRemote *process, - const char *payload, - size_t packet_length, - StringExtractorGDBRemote &response -) -{ - - m_curr_tid = LLDB_INVALID_THREAD_ID; - Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); - if (log) - log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); - - // we want to lock down packet sending while we continue - Mutex::Locker locker(m_sequence_mutex); - - // here we broadcast this before we even send the packet!! - // this signals doContinue() to exit - BroadcastEvent(eBroadcastBitRunPacketSent, NULL); - - // set the public state to running - m_public_is_running.SetValue(true, eBroadcastNever); - - // Set the starting continue packet into "continue_packet". This packet - // may change if we are interrupted and we continue after an async packet... - std::string continue_packet(payload, packet_length); - - if (log) - log->Printf("GDBRemoteCommunicationClient::%s () sending vCont packet: %s", __FUNCTION__, continue_packet.c_str()); - - if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success) - return false; - - // set the private state to running and broadcast this - m_private_is_running.SetValue(true, eBroadcastAlways); - +GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses( + const char *payload_prefix, std::string &response_string) { + Lock lock(*this, false); + if (!lock) { + Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | + GDBR_LOG_PACKETS)); if (log) - log->Printf("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); - - // wait for the response to the vCont - if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return true; - } - - return false; -} - -StateType -GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse -( - ProcessGDBRemote *process, - const char *payload, - size_t packet_length, - StringExtractorGDBRemote &response -) -{ - m_curr_tid = LLDB_INVALID_THREAD_ID; - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); - - Mutex::Locker locker(m_sequence_mutex); - StateType state = eStateRunning; - - m_public_is_running.SetValue (true, eBroadcastNever); - // Set the starting continue packet into "continue_packet". This packet - // may change if we are interrupted and we continue after an async packet... - std::string continue_packet(payload, packet_length); - - const auto sigstop_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGSTOP"); - const auto sigint_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGINT"); - - bool got_async_packet = false; - bool broadcast_sent = false; - - while (state == eStateRunning) - { - if (!got_async_packet) - { - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () sending continue packet: %s", __FUNCTION__, continue_packet.c_str()); - if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success) - state = eStateInvalid; - else - m_interrupt_sent = false; - - if (! broadcast_sent) - { - BroadcastEvent(eBroadcastBitRunPacketSent, NULL); - broadcast_sent = true; - } - - m_private_is_running.SetValue (true, eBroadcastAlways); - } - - got_async_packet = false; - - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); - - if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) - { - if (response.Empty()) - state = eStateInvalid; - else - { - const char stop_type = response.GetChar(); - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str()); - switch (stop_type) - { - case 'T': - case 'S': - { - if (process->GetStopID() == 0) - { - if (process->GetID() == LLDB_INVALID_PROCESS_ID) - { - lldb::pid_t pid = GetCurrentProcessID (); - if (pid != LLDB_INVALID_PROCESS_ID) - process->SetID (pid); - } - process->BuildDynamicRegisterInfo (true); - } - - // Privately notify any internal threads that we have stopped - // in case we wanted to interrupt our process, yet we might - // send a packet and continue without returning control to the - // user. - m_private_is_running.SetValue (false, eBroadcastAlways); - - const uint8_t signo = response.GetHexU8 (UINT8_MAX); - - bool continue_after_async = m_async_signal != -1 || m_async_packet_predicate.GetValue(); - if (continue_after_async || m_interrupt_sent) - { - // We sent an interrupt packet to stop the inferior process - // for an async signal or to send an async packet while running - // but we might have been single stepping and received the - // stop packet for the step instead of for the interrupt packet. - // Typically when an interrupt is sent a SIGINT or SIGSTOP - // is used, so if we get anything else, we need to try and - // get another stop reply packet that may have been sent - // due to sending the interrupt when the target is stopped - // which will just re-send a copy of the last stop reply - // packet. If we don't do this, then the reply for our - // async packet will be the repeat stop reply packet and cause - // a lot of trouble for us! We also have some debugserver - // binaries that would send two stop replies anytime the process - // was interrupted, so we need to also check for an extra - // stop reply packet if we interrupted the process - const bool received_nonstop_signal = signo != sigint_signo && signo != sigstop_signo; - if (m_interrupt_sent || received_nonstop_signal) - { - if (received_nonstop_signal) - continue_after_async = false; - - // Try for a very brief time (0.1s) to get another stop reply - // packet to make sure it doesn't get in the way - StringExtractorGDBRemote extra_stop_reply_packet; - uint32_t timeout_usec = 100000; - if (ReadPacket (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success) - { - switch (extra_stop_reply_packet.GetChar()) - { - case 'T': - case 'S': - // We did get an extra stop reply, which means - // our interrupt didn't stop the target so we - // shouldn't continue after the async signal - // or packet is sent... - continue_after_async = false; - break; - } - } - } - } - - if (m_async_signal != -1) - { - if (log) - log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal)); - - // Save off the async signal we are supposed to send - const int async_signal = m_async_signal; - // Clear the async signal member so we don't end up - // sending the signal multiple times... - m_async_signal = -1; - // Check which signal we stopped with - if (signo == async_signal) - { - if (log) - log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo)); - - // We already stopped with a signal that we wanted - // to stop with, so we are done - } - else - { - // We stopped with a different signal that the one - // we wanted to stop with, so now we must resume - // with the signal we want - char signal_packet[32]; - int signal_packet_len = 0; - signal_packet_len = ::snprintf (signal_packet, - sizeof (signal_packet), - "C%2.2x", - async_signal); - - if (log) - log->Printf ("async: stopped with signal %s, resume with %s", - Host::GetSignalAsCString (signo), - Host::GetSignalAsCString (async_signal)); - - // Set the continue packet to resume even if the - // interrupt didn't cause our stop (ignore continue_after_async) - continue_packet.assign(signal_packet, signal_packet_len); - continue; - } - } - else if (m_async_packet_predicate.GetValue()) - { - Log * packet_log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); - - // We are supposed to send an asynchronous packet while - // we are running. - m_async_response.Clear(); - if (m_async_packet.empty()) - { - m_async_result = PacketResult::ErrorSendFailed; - if (packet_log) - packet_log->Printf ("async: error: empty async packet"); - - } - else - { - if (packet_log) - packet_log->Printf ("async: sending packet"); - - m_async_result = SendPacketAndWaitForResponse (&m_async_packet[0], - m_async_packet.size(), - m_async_response, - false); - } - // Let the other thread that was trying to send the async - // packet know that the packet has been sent and response is - // ready... - m_async_packet_predicate.SetValue(false, eBroadcastAlways); - - if (packet_log) - packet_log->Printf ("async: sent packet, continue_after_async = %i", continue_after_async); - - // Set the continue packet to resume if our interrupt - // for the async packet did cause the stop - if (continue_after_async) - { - // Reverting this for now as it is causing deadlocks - // in programs (<rdar://problem/11529853>). In the future - // we should check our thread list and "do the right thing" - // for new threads that show up while we stop and run async - // packets. Setting the packet to 'c' to continue all threads - // is the right thing to do 99.99% of the time because if a - // thread was single stepping, and we sent an interrupt, we - // will notice above that we didn't stop due to an interrupt - // but stopped due to stepping and we would _not_ continue. - continue_packet.assign (1, 'c'); - continue; - } - } - // Stop with signal and thread info - state = eStateStopped; - } - break; - - case 'W': - case 'X': - // process exited - state = eStateExited; - break; - - case 'O': - // STDOUT - { - got_async_packet = true; - std::string inferior_stdout; - inferior_stdout.reserve(response.GetBytesLeft () / 2); - - uint8_t ch; - while (response.GetHexU8Ex(ch)) - { - if (ch != 0) - inferior_stdout.append(1, (char)ch); - } - process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size()); - } - break; - - case 'A': - // Async miscellaneous reply. Right now, only profile data is coming through this channel. - { - got_async_packet = true; - std::string input = response.GetStringRef().substr(1); // '1' to move beyond 'A' - if (m_partial_profile_data.length() > 0) - { - m_partial_profile_data.append(input); - input = m_partial_profile_data; - m_partial_profile_data.clear(); - } - - size_t found, pos = 0, len = input.length(); - while ((found = input.find(end_delimiter, pos)) != std::string::npos) - { - StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str()); - std::string profile_data = HarmonizeThreadIdsForProfileData(process, profileDataExtractor); - process->BroadcastAsyncProfileData (profile_data); - - pos = found + end_delimiter_len; - } - - if (pos < len) - { - // Last incomplete chunk. - m_partial_profile_data = input.substr(pos); - } - } - break; - - case 'E': - // ERROR - state = eStateInvalid; - break; - - default: - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () unrecognized async packet", __FUNCTION__); - state = eStateInvalid; - break; - } - } - } - else - { - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(...) => false", __FUNCTION__); - state = eStateInvalid; - } - } - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () => %s", __FUNCTION__, StateAsCString(state)); - response.SetFilePos(0); - m_private_is_running.SetValue (false, eBroadcastAlways); - m_public_is_running.SetValue (false, eBroadcastAlways); - return state; -} - -bool -GDBRemoteCommunicationClient::SendAsyncSignal (int signo) -{ - std::lock_guard<std::recursive_mutex> guard(m_async_mutex); - m_async_signal = signo; - bool timed_out = false; - Mutex::Locker locker; - if (SendInterrupt (locker, 1, timed_out)) - return true; - m_async_signal = -1; - return false; -} - -// This function takes a mutex locker as a parameter in case the GetSequenceMutex -// actually succeeds. If it doesn't succeed in acquiring the sequence mutex -// (the expected result), then it will send the halt packet. If it does succeed -// then the caller that requested the interrupt will want to keep the sequence -// locked down so that no one else can send packets while the caller has control. -// This function usually gets called when we are running and need to stop the -// target. It can also be used when we are running and we need to do something -// else (like read/write memory), so we need to interrupt the running process -// (gdb remote protocol requires this), and do what we need to do, then resume. - -bool -GDBRemoteCommunicationClient::SendInterrupt -( - Mutex::Locker& locker, - uint32_t seconds_to_wait_for_stop, - bool &timed_out -) -{ - timed_out = false; - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); - - if (IsRunning()) - { - // Only send an interrupt if our debugserver is running... - if (GetSequenceMutex (locker)) - { - if (log) - log->Printf ("SendInterrupt () - got sequence mutex without having to interrupt"); - } - else - { - // Someone has the mutex locked waiting for a response or for the - // inferior to stop, so send the interrupt on the down low... - char ctrl_c = '\x03'; - ConnectionStatus status = eConnectionStatusSuccess; - size_t bytes_written = Write (&ctrl_c, 1, status, NULL); - if (log) - log->PutCString("send packet: \\x03"); - if (bytes_written > 0) - { - m_interrupt_sent = true; - if (seconds_to_wait_for_stop) - { - TimeValue timeout; - if (seconds_to_wait_for_stop) - { - timeout = TimeValue::Now(); - timeout.OffsetWithSeconds (seconds_to_wait_for_stop); - } - if (m_private_is_running.WaitForValueEqualTo (false, &timeout, &timed_out)) - { - if (log) - log->PutCString ("SendInterrupt () - sent interrupt, private state stopped"); - return true; - } - else - { - if (log) - log->Printf ("SendInterrupt () - sent interrupt, timed out wating for async thread resume"); - } - } - else - { - if (log) - log->Printf ("SendInterrupt () - sent interrupt, not waiting for stop..."); - return true; - } - } - else - { - if (log) - log->Printf ("SendInterrupt () - failed to write interrupt"); - } - return false; - } - } - else - { - if (log) - log->Printf ("SendInterrupt () - not running"); - } - return true; -} - -lldb::pid_t -GDBRemoteCommunicationClient::GetCurrentProcessID (bool allow_lazy) -{ - if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) - return m_curr_pid; - - // First try to retrieve the pid via the qProcessInfo request. - GetCurrentProcessInfo (allow_lazy); - if (m_curr_pid_is_valid == eLazyBoolYes) - { - // We really got it. + log->Printf("error: failed to get packet sequence mutex, not sending " + "packets with prefix '%s'", + payload_prefix); + return PacketResult::ErrorNoSequenceLock; + } + + response_string = ""; + std::string payload_prefix_str(payload_prefix); + unsigned int response_size = 0x1000; + if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet + response_size = GetRemoteMaxPacketSize(); + } + + for (unsigned int offset = 0; true; offset += response_size) { + StringExtractorGDBRemote this_response; + // Construct payload + char sizeDescriptor[128]; + snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, + response_size); + PacketResult result = SendPacketAndWaitForResponseNoLock( + payload_prefix_str + sizeDescriptor, this_response); + if (result != PacketResult::Success) + return result; + + const std::string &this_string = this_response.GetStringRef(); + + // Check for m or l as first character; l seems to mean this is the last + // chunk + char first_char = *this_string.c_str(); + if (first_char != 'm' && first_char != 'l') { + return PacketResult::ErrorReplyInvalid; + } + // Concatenate the result so far (skipping 'm' or 'l') + response_string.append(this_string, 1, std::string::npos); + if (first_char == 'l') + // We're done + return PacketResult::Success; + } +} + +lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { + if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) + return m_curr_pid; + + // First try to retrieve the pid via the qProcessInfo request. + GetCurrentProcessInfo(allow_lazy); + if (m_curr_pid_is_valid == eLazyBoolYes) { + // We really got it. + return m_curr_pid; + } else { + // If we don't get a response for qProcessInfo, check if $qC gives us a + // result. + // $qC only returns a real process id on older debugserver and lldb-platform + // stubs. + // The gdb remote protocol documents $qC as returning the thread id, which + // newer + // debugserver and lldb-gdbserver stubs return correctly. + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", response, false) == + PacketResult::Success) { + if (response.GetChar() == 'Q') { + if (response.GetChar() == 'C') { + m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID); + if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { + m_curr_pid_is_valid = eLazyBoolYes; + return m_curr_pid; + } + } + } + } + + // If we don't get a response for $qC, check if $qfThreadID gives us a + // result. + if (m_curr_pid == LLDB_INVALID_PROCESS_ID) { + std::vector<lldb::tid_t> thread_ids; + bool sequence_mutex_unavailable; + size_t size; + size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable); + if (size && sequence_mutex_unavailable == false) { + m_curr_pid = thread_ids.front(); + m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; + } + } + } + + return LLDB_INVALID_PROCESS_ID; +} + +bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { + error_str.clear(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return true; + if (response.GetChar() == 'E') { + // A string the describes what failed when launching... + error_str = response.GetStringRef().substr(1); + } else { + error_str.assign("unknown error occurred launching process"); + } + } else { + error_str.assign("timed out waiting for app to launch"); + } + return false; +} + +int GDBRemoteCommunicationClient::SendArgumentsPacket( + const ProcessLaunchInfo &launch_info) { + // Since we don't get the send argv0 separate from the executable path, we + // need to + // make sure to use the actual executable path found in the launch_info... + std::vector<const char *> argv; + FileSpec exe_file = launch_info.GetExecutableFile(); + std::string exe_path; + const char *arg = NULL; + const Args &launch_args = launch_info.GetArguments(); + if (exe_file) + exe_path = exe_file.GetPath(false); + else { + arg = launch_args.GetArgumentAtIndex(0); + if (arg) + exe_path = arg; + } + if (!exe_path.empty()) { + argv.push_back(exe_path.c_str()); + for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; + ++i) { + if (arg) + argv.push_back(arg); + } + } + if (!argv.empty()) { + StreamString packet; + packet.PutChar('A'); + for (size_t i = 0, n = argv.size(); i < n; ++i) { + arg = argv[i]; + const int arg_len = strlen(arg); + if (i > 0) + packet.PutChar(','); + packet.Printf("%i,%i,", arg_len * 2, (int)i); + packet.PutBytesAsRawHex8(arg, arg_len); } - else - { - // If we don't get a response for qProcessInfo, check if $qC gives us a result. - // $qC only returns a real process id on older debugserver and lldb-platform stubs. - // The gdb remote protocol documents $qC as returning the thread id, which newer - // debugserver and lldb-gdbserver stubs return correctly. - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false) == PacketResult::Success) - { - if (response.GetChar() == 'Q') - { - if (response.GetChar() == 'C') - { - m_curr_pid = response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); - if (m_curr_pid != LLDB_INVALID_PROCESS_ID) - { - m_curr_pid_is_valid = eLazyBoolYes; - return m_curr_pid; - } - } - } - } - - // If we don't get a response for $qC, check if $qfThreadID gives us a result. - if (m_curr_pid == LLDB_INVALID_PROCESS_ID) - { - std::vector<lldb::tid_t> thread_ids; - bool sequence_mutex_unavailable; - size_t size; - size = GetCurrentThreadIDs (thread_ids, sequence_mutex_unavailable); - if (size && sequence_mutex_unavailable == false) - { - m_curr_pid = thread_ids.front(); - m_curr_pid_is_valid = eLazyBoolYes; - return m_curr_pid; - } - } - } - - return LLDB_INVALID_PROCESS_ID; -} -bool -GDBRemoteCommunicationClient::GetLaunchSuccess (std::string &error_str) -{ - error_str.clear(); StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return true; - if (response.GetChar() == 'E') - { - // A string the describes what failed when launching... - error_str = response.GetStringRef().substr(1); - } - else - { - error_str.assign ("unknown error occurred launching process"); - } - } - else - { - error_str.assign ("timed out waiting for app to launch"); + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return false; + } + return -1; } -int -GDBRemoteCommunicationClient::SendArgumentsPacket (const ProcessLaunchInfo &launch_info) -{ - // Since we don't get the send argv0 separate from the executable path, we need to - // make sure to use the actual executable path found in the launch_info... - std::vector<const char *> argv; - FileSpec exe_file = launch_info.GetExecutableFile(); - std::string exe_path; - const char *arg = NULL; - const Args &launch_args = launch_info.GetArguments(); - if (exe_file) - exe_path = exe_file.GetPath(false); - else - { - arg = launch_args.GetArgumentAtIndex(0); - if (arg) - exe_path = arg; - } - if (!exe_path.empty()) - { - argv.push_back(exe_path.c_str()); - for (uint32_t i=1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; ++i) - { - if (arg) - argv.push_back(arg); +int GDBRemoteCommunicationClient::SendEnvironmentPacket( + char const *name_equal_value) { + if (name_equal_value && name_equal_value[0]) { + StreamString packet; + bool send_hex_encoding = false; + for (const char *p = name_equal_value; + *p != '\0' && send_hex_encoding == false; ++p) { + if (isprint(*p)) { + switch (*p) { + case '$': + case '#': + case '*': + case '}': + send_hex_encoding = true; + break; + default: + break; } + } else { + // We have non printable characters, lets hex encode this... + send_hex_encoding = true; + } } - if (!argv.empty()) - { - StreamString packet; - packet.PutChar('A'); - for (size_t i = 0, n = argv.size(); i < n; ++i) - { - arg = argv[i]; - const int arg_len = strlen(arg); - if (i > 0) - packet.PutChar(','); - packet.Printf("%i,%i,", arg_len * 2, (int)i); - packet.PutBytesAsRawHex8 (arg, arg_len); - } - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; + StringExtractorGDBRemote response; + if (send_hex_encoding) { + if (m_supports_QEnvironmentHexEncoded) { + packet.PutCString("QEnvironmentHexEncoded:"); + packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironmentHexEncoded = false; } - } - return -1; -} + } -int -GDBRemoteCommunicationClient::SendEnvironmentPacket (char const *name_equal_value) -{ - if (name_equal_value && name_equal_value[0]) - { - StreamString packet; - bool send_hex_encoding = false; - for (const char *p = name_equal_value; *p != '\0' && send_hex_encoding == false; ++p) - { - if (isprint(*p)) - { - switch (*p) - { - case '$': - case '#': - case '*': - case '}': - send_hex_encoding = true; - break; - default: - break; - } - } - else - { - // We have non printable characters, lets hex encode this... - send_hex_encoding = true; - } - } - - StringExtractorGDBRemote response; - if (send_hex_encoding) - { - if (m_supports_QEnvironmentHexEncoded) - { - packet.PutCString("QEnvironmentHexEncoded:"); - packet.PutBytesAsRawHex8 (name_equal_value, strlen(name_equal_value)); - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - if (response.IsUnsupportedResponse()) - m_supports_QEnvironmentHexEncoded = false; - } - } - - } - else if (m_supports_QEnvironment) - { - packet.Printf("QEnvironment:%s", name_equal_value); - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - if (response.IsUnsupportedResponse()) - m_supports_QEnvironment = false; - } - } + } else if (m_supports_QEnvironment) { + packet.Printf("QEnvironment:%s", name_equal_value); + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironment = false; + } } - return -1; + } + return -1; } -int -GDBRemoteCommunicationClient::SendLaunchArchPacket (char const *arch) -{ - if (arch && arch[0]) - { - StreamString packet; - packet.Printf("QLaunchArch:%s", arch); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - } +int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) { + if (arch && arch[0]) { + StreamString packet; + packet.Printf("QLaunchArch:%s", arch); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return -1; + } + return -1; } -int -GDBRemoteCommunicationClient::SendLaunchEventDataPacket (char const *data, bool *was_supported) -{ - if (data && *data != '\0') - { - StreamString packet; - packet.Printf("QSetProcessEvent:%s", data); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - if (was_supported) - *was_supported = true; - return 0; - } - else if (response.IsUnsupportedResponse()) - { - if (was_supported) - *was_supported = false; - return -1; - } - else - { - uint8_t error = response.GetError(); - if (was_supported) - *was_supported = true; - if (error) - return error; - } - } +int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( + char const *data, bool *was_supported) { + if (data && *data != '\0') { + StreamString packet; + packet.Printf("QSetProcessEvent:%s", data); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + if (was_supported) + *was_supported = true; + return 0; + } else if (response.IsUnsupportedResponse()) { + if (was_supported) + *was_supported = false; + return -1; + } else { + uint8_t error = response.GetError(); + if (was_supported) + *was_supported = true; + if (error) + return error; + } } - return -1; + } + return -1; } -bool -GDBRemoteCommunicationClient::GetOSVersion (uint32_t &major, - uint32_t &minor, - uint32_t &update) -{ - if (GetHostInfo ()) - { - if (m_os_version_major != UINT32_MAX) - { - major = m_os_version_major; - minor = m_os_version_minor; - update = m_os_version_update; - return true; - } +bool GDBRemoteCommunicationClient::GetOSVersion(uint32_t &major, + uint32_t &minor, + uint32_t &update) { + if (GetHostInfo()) { + if (m_os_version_major != UINT32_MAX) { + major = m_os_version_major; + minor = m_os_version_minor; + update = m_os_version_update; + return true; } - return false; + } + return false; } -bool -GDBRemoteCommunicationClient::GetOSBuildString (std::string &s) -{ - if (GetHostInfo ()) - { - if (!m_os_build.empty()) - { - s = m_os_build; - return true; - } +bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { + if (GetHostInfo()) { + if (!m_os_build.empty()) { + s = m_os_build; + return true; } - s.clear(); - return false; + } + s.clear(); + return false; } - -bool -GDBRemoteCommunicationClient::GetOSKernelDescription (std::string &s) -{ - if (GetHostInfo ()) - { - if (!m_os_kernel.empty()) - { - s = m_os_kernel; - return true; - } +bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) { + if (GetHostInfo()) { + if (!m_os_kernel.empty()) { + s = m_os_kernel; + return true; } - s.clear(); - return false; + } + s.clear(); + return false; } -bool -GDBRemoteCommunicationClient::GetHostname (std::string &s) -{ - if (GetHostInfo ()) - { - if (!m_hostname.empty()) - { - s = m_hostname; - return true; - } +bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { + if (GetHostInfo()) { + if (!m_hostname.empty()) { + s = m_hostname; + return true; } - s.clear(); - return false; + } + s.clear(); + return false; } -ArchSpec -GDBRemoteCommunicationClient::GetSystemArchitecture () -{ - if (GetHostInfo ()) - return m_host_arch; - return ArchSpec(); +ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() { + if (GetHostInfo()) + return m_host_arch; + return ArchSpec(); } const lldb_private::ArchSpec & -GDBRemoteCommunicationClient::GetProcessArchitecture () -{ - if (m_qProcessInfo_is_valid == eLazyBoolCalculate) - GetCurrentProcessInfo (); - return m_process_arch; +GDBRemoteCommunicationClient::GetProcessArchitecture() { + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo(); + return m_process_arch; } -bool -GDBRemoteCommunicationClient::GetGDBServerVersion() -{ - if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) - { - m_gdb_server_name.clear(); - m_gdb_server_version = 0; - m_qGDBServerVersion_is_valid = eLazyBoolNo; - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse ("qGDBServerVersion", response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - { - std::string name; - std::string value; - bool success = false; - while (response.GetNameColonValue(name, value)) - { - if (name.compare("name") == 0) - { - success = true; - m_gdb_server_name.swap(value); - } - else if (name.compare("version") == 0) - { - size_t dot_pos = value.find('.'); - if (dot_pos != std::string::npos) - value[dot_pos] = '\0'; - const uint32_t version = StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0); - if (version != UINT32_MAX) - { - success = true; - m_gdb_server_version = version; - } - } - } - if (success) - m_qGDBServerVersion_is_valid = eLazyBoolYes; - } - } - } - return m_qGDBServerVersion_is_valid == eLazyBoolYes; -} +bool GDBRemoteCommunicationClient::GetGDBServerVersion() { + if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { + m_gdb_server_name.clear(); + m_gdb_server_version = 0; + m_qGDBServerVersion_is_valid = eLazyBoolNo; -void -GDBRemoteCommunicationClient::MaybeEnableCompression (std::vector<std::string> supported_compressions) -{ - CompressionType avail_type = CompressionType::None; - std::string avail_name; - -#if defined (HAVE_LIBCOMPRESSION) - // libcompression is weak linked so test if compression_decode_buffer() is available - if (compression_decode_buffer != NULL && avail_type == CompressionType::None) - { - for (auto compression : supported_compressions) - { - if (compression == "lzfse") - { - avail_type = CompressionType::LZFSE; - avail_name = compression; - break; - } - } - } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name, value; + bool success = false; + while (response.GetNameColonValue(name, value)) { + if (name.equals("name")) { + success = true; + m_gdb_server_name = value; + } else if (name.equals("version")) { + llvm::StringRef major, minor; + std::tie(major, minor) = value.split('.'); + if (!major.getAsInteger(0, m_gdb_server_version)) + success = true; + } + } + if (success) + m_qGDBServerVersion_is_valid = eLazyBoolYes; + } + } + } + return m_qGDBServerVersion_is_valid == eLazyBoolYes; +} + +void GDBRemoteCommunicationClient::MaybeEnableCompression( + std::vector<std::string> supported_compressions) { + CompressionType avail_type = CompressionType::None; + std::string avail_name; + +#if defined(HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is + // available + if (compression_decode_buffer != NULL && + avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lzfse") { + avail_type = CompressionType::LZFSE; + avail_name = compression; + break; + } + } + } #endif -#if defined (HAVE_LIBCOMPRESSION) - // libcompression is weak linked so test if compression_decode_buffer() is available - if (compression_decode_buffer != NULL && avail_type == CompressionType::None) - { - for (auto compression : supported_compressions) - { - if (compression == "zlib-deflate") - { - avail_type = CompressionType::ZlibDeflate; - avail_name = compression; - break; - } - } - } +#if defined(HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is + // available + if (compression_decode_buffer != NULL && + avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "zlib-deflate") { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } + } + } #endif -#if defined (HAVE_LIBZ) - if (avail_type == CompressionType::None) - { - for (auto compression : supported_compressions) - { - if (compression == "zlib-deflate") - { - avail_type = CompressionType::ZlibDeflate; - avail_name = compression; - break; - } - } +#if defined(HAVE_LIBZ) + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "zlib-deflate") { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } } + } #endif -#if defined (HAVE_LIBCOMPRESSION) - // libcompression is weak linked so test if compression_decode_buffer() is available - if (compression_decode_buffer != NULL && avail_type == CompressionType::None) - { - for (auto compression : supported_compressions) - { - if (compression == "lz4") - { - avail_type = CompressionType::LZ4; - avail_name = compression; - break; - } - } - } +#if defined(HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is + // available + if (compression_decode_buffer != NULL && + avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lz4") { + avail_type = CompressionType::LZ4; + avail_name = compression; + break; + } + } + } #endif -#if defined (HAVE_LIBCOMPRESSION) - // libcompression is weak linked so test if compression_decode_buffer() is available - if (compression_decode_buffer != NULL && avail_type == CompressionType::None) - { - for (auto compression : supported_compressions) - { - if (compression == "lzma") - { - avail_type = CompressionType::LZMA; - avail_name = compression; - break; - } - } - } +#if defined(HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is + // available + if (compression_decode_buffer != NULL && + avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lzma") { + avail_type = CompressionType::LZMA; + avail_name = compression; + break; + } + } + } #endif - if (avail_type != CompressionType::None) - { - StringExtractorGDBRemote response; - std::string packet = "QEnableCompression:type:" + avail_name + ";"; - if (SendPacketAndWaitForResponse (packet.c_str(), response, false) != PacketResult::Success) - return; - - if (response.IsOKResponse()) - { - m_compression_type = avail_type; - } - } -} + if (avail_type != CompressionType::None) { + StringExtractorGDBRemote response; + std::string packet = "QEnableCompression:type:" + avail_name + ";"; + if (SendPacketAndWaitForResponse(packet, response, false) != + PacketResult::Success) + return; -const char * -GDBRemoteCommunicationClient::GetGDBServerProgramName() -{ - if (GetGDBServerVersion()) - { - if (!m_gdb_server_name.empty()) - return m_gdb_server_name.c_str(); + if (response.IsOKResponse()) { + m_compression_type = avail_type; } - return NULL; + } } -uint32_t -GDBRemoteCommunicationClient::GetGDBServerProgramVersion() -{ - if (GetGDBServerVersion()) - return m_gdb_server_version; - return 0; +const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() { + if (GetGDBServerVersion()) { + if (!m_gdb_server_name.empty()) + return m_gdb_server_name.c_str(); + } + return NULL; } -bool -GDBRemoteCommunicationClient::GetDefaultThreadId (lldb::tid_t &tid) -{ - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qC",response,false) != PacketResult::Success) - return false; - - if (!response.IsNormalResponse()) - return false; - - if (response.GetChar() == 'Q' && response.GetChar() == 'C') - tid = response.GetHexMaxU32(true, -1); - - return true; +uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { + if (GetGDBServerVersion()) + return m_gdb_server_version; + return 0; } -bool -GDBRemoteCommunicationClient::GetHostInfo (bool force) -{ - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS)); - - if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) - { - m_qHostInfo_is_valid = eLazyBoolNo; - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse ("qHostInfo", response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - { - std::string name; - std::string value; - uint32_t cpu = LLDB_INVALID_CPUTYPE; - uint32_t sub = 0; - std::string arch_name; - std::string os_name; - std::string vendor_name; - std::string triple; - std::string distribution_id; - uint32_t pointer_byte_size = 0; - StringExtractor extractor; - ByteOrder byte_order = eByteOrderInvalid; - uint32_t num_keys_decoded = 0; - while (response.GetNameColonValue(name, value)) - { - if (name.compare("cputype") == 0) - { - // exception type in big endian hex - cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0); - if (cpu != LLDB_INVALID_CPUTYPE) - ++num_keys_decoded; - } - else if (name.compare("cpusubtype") == 0) - { - // exception count in big endian hex - sub = StringConvert::ToUInt32 (value.c_str(), 0, 0); - if (sub != 0) - ++num_keys_decoded; - } - else if (name.compare("arch") == 0) - { - arch_name.swap (value); - ++num_keys_decoded; - } - else if (name.compare("triple") == 0) - { - extractor.GetStringRef ().swap (value); - extractor.SetFilePos(0); - extractor.GetHexByteString (triple); - ++num_keys_decoded; - } - else if (name.compare ("distribution_id") == 0) - { - extractor.GetStringRef ().swap (value); - extractor.SetFilePos (0); - extractor.GetHexByteString (distribution_id); - ++num_keys_decoded; - } - else if (name.compare("os_build") == 0) - { - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (m_os_build); - ++num_keys_decoded; - } - else if (name.compare("hostname") == 0) - { - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (m_hostname); - ++num_keys_decoded; - } - else if (name.compare("os_kernel") == 0) - { - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (m_os_kernel); - ++num_keys_decoded; - } - else if (name.compare("ostype") == 0) - { - os_name.swap (value); - ++num_keys_decoded; - } - else if (name.compare("vendor") == 0) - { - vendor_name.swap(value); - ++num_keys_decoded; - } - else if (name.compare("endian") == 0) - { - ++num_keys_decoded; - if (value.compare("little") == 0) - byte_order = eByteOrderLittle; - else if (value.compare("big") == 0) - byte_order = eByteOrderBig; - else if (value.compare("pdp") == 0) - byte_order = eByteOrderPDP; - else - --num_keys_decoded; - } - else if (name.compare("ptrsize") == 0) - { - pointer_byte_size = StringConvert::ToUInt32 (value.c_str(), 0, 0); - if (pointer_byte_size != 0) - ++num_keys_decoded; - } - else if ((name.compare("os_version") == 0) || - (name.compare("version") == 0)) // Older debugserver binaries used the "version" key instead of "os_version"... - { - Args::StringToVersion (value.c_str(), - m_os_version_major, - m_os_version_minor, - m_os_version_update); - if (m_os_version_major != UINT32_MAX) - ++num_keys_decoded; - } - else if (name.compare("watchpoint_exceptions_received") == 0) - { - ++num_keys_decoded; - if (strcmp(value.c_str(),"before") == 0) - m_watchpoints_trigger_after_instruction = eLazyBoolNo; - else if (strcmp(value.c_str(),"after") == 0) - m_watchpoints_trigger_after_instruction = eLazyBoolYes; - else - --num_keys_decoded; - } - else if (name.compare("default_packet_timeout") == 0) - { - m_default_packet_timeout = StringConvert::ToUInt32(value.c_str(), 0); - if (m_default_packet_timeout > 0) - { - SetPacketTimeout(m_default_packet_timeout); - ++num_keys_decoded; - } - } +bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", response, false) != + PacketResult::Success) + return false; - } - - if (num_keys_decoded > 0) - m_qHostInfo_is_valid = eLazyBoolYes; - - if (triple.empty()) - { - if (arch_name.empty()) - { - if (cpu != LLDB_INVALID_CPUTYPE) - { - m_host_arch.SetArchitecture (eArchTypeMachO, cpu, sub); - if (pointer_byte_size) - { - assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); - } - if (byte_order != eByteOrderInvalid) - { - assert (byte_order == m_host_arch.GetByteOrder()); - } - - if (!vendor_name.empty()) - m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); - if (!os_name.empty()) - m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); - - } - } - else - { - std::string triple; - triple += arch_name; - if (!vendor_name.empty() || !os_name.empty()) - { - triple += '-'; - if (vendor_name.empty()) - triple += "unknown"; - else - triple += vendor_name; - triple += '-'; - if (os_name.empty()) - triple += "unknown"; - else - triple += os_name; - } - m_host_arch.SetTriple (triple.c_str()); - - llvm::Triple &host_triple = m_host_arch.GetTriple(); - if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin) - { - switch (m_host_arch.GetMachine()) - { - case llvm::Triple::aarch64: - case llvm::Triple::arm: - case llvm::Triple::thumb: - host_triple.setOS(llvm::Triple::IOS); - break; - default: - host_triple.setOS(llvm::Triple::MacOSX); - break; - } - } - if (pointer_byte_size) - { - assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); - } - if (byte_order != eByteOrderInvalid) - { - assert (byte_order == m_host_arch.GetByteOrder()); - } - - } - } - else - { - m_host_arch.SetTriple (triple.c_str()); - if (pointer_byte_size) - { - assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); - } - if (byte_order != eByteOrderInvalid) - { - assert (byte_order == m_host_arch.GetByteOrder()); - } + if (!response.IsNormalResponse()) + return false; - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s parsed host architecture as %s, triple as %s from triple text %s", __FUNCTION__, m_host_arch.GetArchitectureName () ? m_host_arch.GetArchitectureName () : "<null-arch-name>", m_host_arch.GetTriple ().getTriple ().c_str(), triple.c_str ()); - } - if (!distribution_id.empty ()) - m_host_arch.SetDistributionId (distribution_id.c_str ()); - } - } - } - return m_qHostInfo_is_valid == eLazyBoolYes; -} + if (response.GetChar() == 'Q' && response.GetChar() == 'C') + tid = response.GetHexMaxU32(true, -1); -int -GDBRemoteCommunicationClient::SendAttach -( - lldb::pid_t pid, - StringExtractorGDBRemote& response -) -{ - if (pid != LLDB_INVALID_PROCESS_ID) - { - char packet[64]; - const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, pid); - assert (packet_len < (int)sizeof(packet)); - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsErrorResponse()) - return response.GetError(); - return 0; - } - } - return -1; + return true; } -int -GDBRemoteCommunicationClient::SendStdinNotification (const char* data, size_t data_len) -{ - StreamString packet; - packet.PutCString("I"); - packet.PutBytesAsRawHex8(data, data_len); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - return 0; - } - return response.GetError(); +bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { + Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS)); + if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { + m_qHostInfo_is_valid = eLazyBoolNo; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qHostInfo", response, false) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string vendor_name; + std::string triple; + std::string distribution_id; + uint32_t pointer_byte_size = 0; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + while (response.GetNameColonValue(name, value)) { + if (name.equals("cputype")) { + // exception type in big endian hex + if (!value.getAsInteger(0, cpu)) + ++num_keys_decoded; + } else if (name.equals("cpusubtype")) { + // exception count in big endian hex + if (!value.getAsInteger(0, sub)) + ++num_keys_decoded; + } else if (name.equals("arch")) { + arch_name = value; + ++num_keys_decoded; + } else if (name.equals("triple")) { + StringExtractor extractor(value); + extractor.GetHexByteString(triple); + ++num_keys_decoded; + } else if (name.equals("distribution_id")) { + StringExtractor extractor(value); + extractor.GetHexByteString(distribution_id); + ++num_keys_decoded; + } else if (name.equals("os_build")) { + StringExtractor extractor(value); + extractor.GetHexByteString(m_os_build); + ++num_keys_decoded; + } else if (name.equals("hostname")) { + StringExtractor extractor(value); + extractor.GetHexByteString(m_hostname); + ++num_keys_decoded; + } else if (name.equals("os_kernel")) { + StringExtractor extractor(value); + extractor.GetHexByteString(m_os_kernel); + ++num_keys_decoded; + } else if (name.equals("ostype")) { + os_name = value; + ++num_keys_decoded; + } else if (name.equals("vendor")) { + vendor_name = value; + ++num_keys_decoded; + } else if (name.equals("endian")) { + byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) + .Case("little", eByteOrderLittle) + .Case("big", eByteOrderBig) + .Case("pdp", eByteOrderPDP) + .Default(eByteOrderInvalid); + if (byte_order != eByteOrderInvalid) + ++num_keys_decoded; + } else if (name.equals("ptrsize")) { + if (!value.getAsInteger(0, pointer_byte_size)) + ++num_keys_decoded; + } else if (name.equals("os_version") || + name.equals( + "version")) // Older debugserver binaries used the + // "version" key instead of + // "os_version"... + { + Args::StringToVersion(value, m_os_version_major, m_os_version_minor, + m_os_version_update); + if (m_os_version_major != UINT32_MAX) + ++num_keys_decoded; + } else if (name.equals("watchpoint_exceptions_received")) { + m_watchpoints_trigger_after_instruction = + llvm::StringSwitch<LazyBool>(value) + .Case("before", eLazyBoolNo) + .Case("after", eLazyBoolYes) + .Default(eLazyBoolCalculate); + if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate) + ++num_keys_decoded; + } else if (name.equals("default_packet_timeout")) { + uint32_t timeout_seconds; + if (!value.getAsInteger(0, timeout_seconds)) { + m_default_packet_timeout = seconds(timeout_seconds); + SetPacketTimeout(m_default_packet_timeout); + ++num_keys_decoded; + } + } + } + + if (num_keys_decoded > 0) + m_qHostInfo_is_valid = eLazyBoolYes; + + if (triple.empty()) { + if (arch_name.empty()) { + if (cpu != LLDB_INVALID_CPUTYPE) { + m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub); + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + + if (!vendor_name.empty()) + m_host_arch.GetTriple().setVendorName( + llvm::StringRef(vendor_name)); + if (!os_name.empty()) + m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); + } + } else { + std::string triple; + triple += arch_name; + if (!vendor_name.empty() || !os_name.empty()) { + triple += '-'; + if (vendor_name.empty()) + triple += "unknown"; + else + triple += vendor_name; + triple += '-'; + if (os_name.empty()) + triple += "unknown"; + else + triple += os_name; + } + m_host_arch.SetTriple(triple.c_str()); + + llvm::Triple &host_triple = m_host_arch.GetTriple(); + if (host_triple.getVendor() == llvm::Triple::Apple && + host_triple.getOS() == llvm::Triple::Darwin) { + switch (m_host_arch.GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::thumb: + host_triple.setOS(llvm::Triple::IOS); + break; + default: + host_triple.setOS(llvm::Triple::MacOSX); + break; + } + } + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + } + } else { + m_host_arch.SetTriple(triple.c_str()); + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + + if (log) + log->Printf("GDBRemoteCommunicationClient::%s parsed host " + "architecture as %s, triple as %s from triple text %s", + __FUNCTION__, m_host_arch.GetArchitectureName() + ? m_host_arch.GetArchitectureName() + : "<null-arch-name>", + m_host_arch.GetTriple().getTriple().c_str(), + triple.c_str()); + } + if (!distribution_id.empty()) + m_host_arch.SetDistributionId(distribution_id.c_str()); + } + } + } + return m_qHostInfo_is_valid == eLazyBoolYes; +} + +int GDBRemoteCommunicationClient::SendAttach( + lldb::pid_t pid, StringExtractorGDBRemote &response) { + if (pid != LLDB_INVALID_PROCESS_ID) { + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid); + UNUSED_IF_ASSERT_DISABLED(packet_len); + assert(packet_len < (int)sizeof(packet)); + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetError(); + return 0; + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, + size_t data_len) { + StreamString packet; + packet.PutCString("I"); + packet.PutBytesAsRawHex8(data, data_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + return 0; + } + return response.GetError(); } const lldb_private::ArchSpec & -GDBRemoteCommunicationClient::GetHostArchitecture () -{ - if (m_qHostInfo_is_valid == eLazyBoolCalculate) - GetHostInfo (); - return m_host_arch; +GDBRemoteCommunicationClient::GetHostArchitecture() { + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + return m_host_arch; } -uint32_t -GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout () -{ - if (m_qHostInfo_is_valid == eLazyBoolCalculate) - GetHostInfo (); - return m_default_packet_timeout; +seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() { + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + return m_default_packet_timeout; } -addr_t -GDBRemoteCommunicationClient::AllocateMemory (size_t size, uint32_t permissions) -{ - if (m_supports_alloc_dealloc_memory != eLazyBoolNo) - { - m_supports_alloc_dealloc_memory = eLazyBoolYes; - char packet[64]; - const int packet_len = ::snprintf (packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", - (uint64_t)size, - permissions & lldb::ePermissionsReadable ? "r" : "", - permissions & lldb::ePermissionsWritable ? "w" : "", - permissions & lldb::ePermissionsExecutable ? "x" : ""); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - m_supports_alloc_dealloc_memory = eLazyBoolNo; - else if (!response.IsErrorResponse()) - return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); - } - else - { - m_supports_alloc_dealloc_memory = eLazyBoolNo; - } - } - return LLDB_INVALID_ADDRESS; +addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size, + uint32_t permissions) { + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf( + packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_alloc_dealloc_memory = eLazyBoolNo; + else if (!response.IsErrorResponse()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } else { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_alloc_dealloc_memory = eLazyBoolNo; + else if (response.IsOKResponse()) + return true; + } else { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return false; +} + +Error GDBRemoteCommunicationClient::Detach(bool keep_stopped) { + Error error; + + if (keep_stopped) { + if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success && + response.IsOKResponse()) { + m_supports_detach_stay_stopped = eLazyBoolYes; + } else { + m_supports_detach_stay_stopped = eLazyBoolNo; + } + } + + if (m_supports_detach_stay_stopped == eLazyBoolNo) { + error.SetErrorString("Stays stopped not supported by this target."); + return error; + } else { + StringExtractorGDBRemote response; + PacketResult packet_result = + SendPacketAndWaitForResponse("D1", response, false); + if (packet_result != PacketResult::Success) + error.SetErrorString("Sending extended disconnect packet failed."); + } + } else { + StringExtractorGDBRemote response; + PacketResult packet_result = + SendPacketAndWaitForResponse("D", response, false); + if (packet_result != PacketResult::Success) + error.SetErrorString("Sending disconnect packet failed."); + } + return error; } -bool -GDBRemoteCommunicationClient::DeallocateMemory (addr_t addr) -{ - if (m_supports_alloc_dealloc_memory != eLazyBoolNo) - { - m_supports_alloc_dealloc_memory = eLazyBoolYes; - char packet[64]; - const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - m_supports_alloc_dealloc_memory = eLazyBoolNo; - else if (response.IsOKResponse()) - return true; - } - else - { - m_supports_alloc_dealloc_memory = eLazyBoolNo; - } - } - return false; -} +Error GDBRemoteCommunicationClient::GetMemoryRegionInfo( + lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { + Error error; + region_info.Clear(); -Error -GDBRemoteCommunicationClient::Detach (bool keep_stopped) -{ - Error error; - - if (keep_stopped) - { - if (m_supports_detach_stay_stopped == eLazyBoolCalculate) - { - char packet[64]; - const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success - && response.IsOKResponse()) - { - m_supports_detach_stay_stopped = eLazyBoolYes; - } + if (m_supports_memory_region_info != eLazyBoolNo) { + m_supports_memory_region_info = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf( + packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + llvm::StringRef name; + llvm::StringRef value; + addr_t addr_value = LLDB_INVALID_ADDRESS; + bool success = true; + bool saw_permissions = false; + while (success && response.GetNameColonValue(name, value)) { + if (name.equals("start")) { + if (!value.getAsInteger(16, addr_value)) + region_info.GetRange().SetRangeBase(addr_value); + } else if (name.equals("size")) { + if (!value.getAsInteger(16, addr_value)) + region_info.GetRange().SetByteSize(addr_value); + } else if (name.equals("permissions") && + region_info.GetRange().IsValid()) { + saw_permissions = true; + if (region_info.GetRange().Contains(addr)) { + if (value.find('r') != llvm::StringRef::npos) + region_info.SetReadable(MemoryRegionInfo::eYes); else - { - m_supports_detach_stay_stopped = eLazyBoolNo; - } - } + region_info.SetReadable(MemoryRegionInfo::eNo); - if (m_supports_detach_stay_stopped == eLazyBoolNo) - { - error.SetErrorString("Stays stopped not supported by this target."); - return error; - } - else - { - StringExtractorGDBRemote response; - PacketResult packet_result = SendPacketAndWaitForResponse ("D1", 2, response, false); - if (packet_result != PacketResult::Success) - error.SetErrorString ("Sending extended disconnect packet failed."); - } - } - else - { - StringExtractorGDBRemote response; - PacketResult packet_result = SendPacketAndWaitForResponse ("D", 1, response, false); - if (packet_result != PacketResult::Success) - error.SetErrorString ("Sending disconnect packet failed."); - } - return error; -} + if (value.find('w') != llvm::StringRef::npos) + region_info.SetWritable(MemoryRegionInfo::eYes); + else + region_info.SetWritable(MemoryRegionInfo::eNo); -Error -GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, - lldb_private::MemoryRegionInfo ®ion_info) -{ - Error error; + if (value.find('x') != llvm::StringRef::npos) + region_info.SetExecutable(MemoryRegionInfo::eYes); + else + region_info.SetExecutable(MemoryRegionInfo::eNo); + + region_info.SetMapped(MemoryRegionInfo::eYes); + } else { + // The reported region does not contain this address -- we're + // looking at an unmapped page + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + } else if (name.equals("name")) { + StringExtractorGDBRemote name_extractor(value); + std::string name; + name_extractor.GetHexByteString(name); + region_info.SetName(name.c_str()); + } else if (name.equals("error")) { + StringExtractorGDBRemote error_extractor(value); + std::string error_string; + // Now convert the HEX bytes into a string value + error_extractor.GetHexByteString(error_string); + error.SetErrorString(error_string.c_str()); + } + } + + // We got a valid address range back but no permissions -- which means + // this is an unmapped page + if (region_info.GetRange().IsValid() && saw_permissions == false) { + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + } else { + m_supports_memory_region_info = eLazyBoolNo; + } + } + + if (m_supports_memory_region_info == eLazyBoolNo) { + error.SetErrorString("qMemoryRegionInfo is not supported"); + } + if (error.Fail()) region_info.Clear(); - - if (m_supports_memory_region_info != eLazyBoolNo) - { - m_supports_memory_region_info = eLazyBoolYes; - char packet[64]; - const int packet_len = ::snprintf(packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - std::string name; - std::string value; - addr_t addr_value; - bool success = true; - bool saw_permissions = false; - while (success && response.GetNameColonValue(name, value)) - { - if (name.compare ("start") == 0) - { - addr_value = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16, &success); - if (success) - region_info.GetRange().SetRangeBase(addr_value); - } - else if (name.compare ("size") == 0) - { - addr_value = StringConvert::ToUInt64(value.c_str(), 0, 16, &success); - if (success) - region_info.GetRange().SetByteSize (addr_value); - } - else if (name.compare ("permissions") == 0 && region_info.GetRange().IsValid()) - { - saw_permissions = true; - if (region_info.GetRange().Contains (addr)) - { - if (value.find('r') != std::string::npos) - region_info.SetReadable (MemoryRegionInfo::eYes); - else - region_info.SetReadable (MemoryRegionInfo::eNo); - - if (value.find('w') != std::string::npos) - region_info.SetWritable (MemoryRegionInfo::eYes); - else - region_info.SetWritable (MemoryRegionInfo::eNo); - - if (value.find('x') != std::string::npos) - region_info.SetExecutable (MemoryRegionInfo::eYes); - else - region_info.SetExecutable (MemoryRegionInfo::eNo); - - region_info.SetMapped(MemoryRegionInfo::eYes); - } - else - { - // The reported region does not contain this address -- we're looking at an unmapped page - region_info.SetReadable (MemoryRegionInfo::eNo); - region_info.SetWritable (MemoryRegionInfo::eNo); - region_info.SetExecutable (MemoryRegionInfo::eNo); - region_info.SetMapped(MemoryRegionInfo::eNo); - } - } - else if (name.compare ("error") == 0) - { - StringExtractorGDBRemote 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); - error.SetErrorString(value.c_str()); - } - } - - // We got a valid address range back but no permissions -- which means this is an unmapped page - if (region_info.GetRange().IsValid() && saw_permissions == false) - { - region_info.SetReadable (MemoryRegionInfo::eNo); - region_info.SetWritable (MemoryRegionInfo::eNo); - region_info.SetExecutable (MemoryRegionInfo::eNo); - region_info.SetMapped(MemoryRegionInfo::eNo); - } - } - else - { - m_supports_memory_region_info = eLazyBoolNo; - } - } - - if (m_supports_memory_region_info == eLazyBoolNo) - { - error.SetErrorString("qMemoryRegionInfo is not supported"); - } - if (error.Fail()) - region_info.Clear(); - return error; - + return error; } -Error -GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num) -{ - Error error; +Error GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) { + Error error; - if (m_supports_watchpoint_support_info == eLazyBoolYes) - { - num = m_num_supported_hardware_watchpoints; - return error; - } + if (m_supports_watchpoint_support_info == eLazyBoolYes) { + num = m_num_supported_hardware_watchpoints; + return error; + } - // Set num to 0 first. - num = 0; - if (m_supports_watchpoint_support_info != eLazyBoolNo) - { - char packet[64]; - const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - m_supports_watchpoint_support_info = eLazyBoolYes; - std::string name; - std::string value; - while (response.GetNameColonValue(name, value)) - { - if (name.compare ("num") == 0) - { - num = StringConvert::ToUInt32(value.c_str(), 0, 0); - m_num_supported_hardware_watchpoints = num; - } - } - } - else - { - m_supports_watchpoint_support_info = eLazyBoolNo; + // Set num to 0 first. + num = 0; + if (m_supports_watchpoint_support_info != eLazyBoolNo) { + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + m_supports_watchpoint_support_info = eLazyBoolYes; + llvm::StringRef name; + llvm::StringRef value; + while (response.GetNameColonValue(name, value)) { + if (name.equals("num")) { + value.getAsInteger(0, m_num_supported_hardware_watchpoints); + num = m_num_supported_hardware_watchpoints; } + } + } else { + m_supports_watchpoint_support_info = eLazyBoolNo; } + } - if (m_supports_watchpoint_support_info == eLazyBoolNo) - { - error.SetErrorString("qWatchpointSupportInfo is not supported"); - } - return error; - + if (m_supports_watchpoint_support_info == eLazyBoolNo) { + error.SetErrorString("qWatchpointSupportInfo is not supported"); + } + return error; } -lldb_private::Error -GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after, const ArchSpec &arch) -{ - Error error(GetWatchpointSupportInfo(num)); - if (error.Success()) - error = GetWatchpointsTriggerAfterInstruction(after, arch); - return error; +lldb_private::Error GDBRemoteCommunicationClient::GetWatchpointSupportInfo( + uint32_t &num, bool &after, const ArchSpec &arch) { + Error error(GetWatchpointSupportInfo(num)); + if (error.Success()) + error = GetWatchpointsTriggerAfterInstruction(after, arch); + return error; } lldb_private::Error -GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after, const ArchSpec &arch) -{ - Error error; - llvm::Triple::ArchType atype = arch.GetMachine(); - - // we assume watchpoints will happen after running the relevant opcode - // and we only want to override this behavior if we have explicitly - // received a qHostInfo telling us otherwise - if (m_qHostInfo_is_valid != eLazyBoolYes) - { - // On targets like MIPS, watchpoint exceptions are always generated - // before the instruction is executed. The connected target may not - // support qHostInfo or qWatchpointSupportInfo packets. - if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel - || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) - after = false; - else - after = true; - } +GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction( + bool &after, const ArchSpec &arch) { + Error error; + llvm::Triple::ArchType atype = arch.GetMachine(); + + // we assume watchpoints will happen after running the relevant opcode + // and we only want to override this behavior if we have explicitly + // received a qHostInfo telling us otherwise + if (m_qHostInfo_is_valid != eLazyBoolYes) { + // On targets like MIPS, watchpoint exceptions are always generated + // before the instruction is executed. The connected target may not + // support qHostInfo or qWatchpointSupportInfo packets. + if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || + atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) + after = false; else - { - // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo - // if it is not calculated before. - if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && - (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel - || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) - m_watchpoints_trigger_after_instruction = eLazyBoolNo; - - after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); - } - return error; -} - -int -GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) -{ - if (file_spec) - { - std::string path{file_spec.GetPath(false)}; - StreamString packet; - packet.PutCString("QSetSTDIN:"); - packet.PutCStringAsRawHex8(path.c_str()); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - } - } - return -1; -} + after = true; + } else { + // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo + // if it is not calculated before. + if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && + (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || + atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) + m_watchpoints_trigger_after_instruction = eLazyBoolNo; + + after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); + } + return error; +} + +int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDIN:"); + packet.PutCStringAsRawHex8(path.c_str()); -int -GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) -{ - if (file_spec) - { - std::string path{file_spec.GetPath(false)}; - StreamString packet; - packet.PutCString("QSetSTDOUT:"); - packet.PutCStringAsRawHex8(path.c_str()); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return -1; + } + return -1; } -int -GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) -{ - if (file_spec) - { - std::string path{file_spec.GetPath(false)}; - StreamString packet; - packet.PutCString("QSetSTDERR:"); - packet.PutCStringAsRawHex8(path.c_str()); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - } - } - return -1; -} +int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDOUT:"); + packet.PutCStringAsRawHex8(path.c_str()); -bool -GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) -{ StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse ("qGetWorkingDir", response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - return false; - if (response.IsErrorResponse()) - return false; - std::string cwd; - response.GetHexByteString(cwd); - working_dir.SetFile(cwd, false, GetHostArchitecture()); - return !cwd.empty(); + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return false; + } + return -1; } -int -GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) -{ - if (working_dir) - { - std::string path{working_dir.GetPath(false)}; - StreamString packet; - packet.PutCString("QSetWorkingDir:"); - packet.PutCStringAsRawHex8(path.c_str()); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - } - } - return -1; -} +int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDERR:"); + packet.PutCStringAsRawHex8(path.c_str()); -int -GDBRemoteCommunicationClient::SetDisableASLR (bool enable) -{ - char packet[32]; - const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDisableASLR:%i", enable ? 1 : 0); - assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return -1; -} + } + return -1; +} + +bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + return false; + if (response.IsErrorResponse()) + return false; + std::string cwd; + response.GetHexByteString(cwd); + working_dir.SetFile(cwd, false, GetHostArchitecture()); + return !cwd.empty(); + } + return false; +} + +int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { + if (working_dir) { + std::string path{working_dir.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetWorkingDir:"); + packet.PutCStringAsRawHex8(path.c_str()); -int -GDBRemoteCommunicationClient::SetDetachOnError (bool enable) -{ - char packet[32]; - const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDetachOnError:%i", enable ? 1 : 0); - assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; } - return -1; -} - + } + return -1; +} + +int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + +int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) { + char packet[32]; + const int packet_len = ::snprintf(packet, sizeof(packet), + "QSetDetachOnError:%i", enable ? 1 : 0); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + +bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( + StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + StringExtractor extractor; -bool -GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) -{ - if (response.IsNormalResponse()) - { + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string vendor; + std::string os_type; + + while (response.GetNameColonValue(name, value)) { + if (name.equals("pid")) { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + value.getAsInteger(0, pid); + process_info.SetProcessID(pid); + } else if (name.equals("ppid")) { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + value.getAsInteger(0, pid); + process_info.SetParentProcessID(pid); + } else if (name.equals("uid")) { + uint32_t uid = UINT32_MAX; + value.getAsInteger(0, uid); + process_info.SetUserID(uid); + } else if (name.equals("euid")) { + uint32_t uid = UINT32_MAX; + value.getAsInteger(0, uid); + process_info.SetEffectiveGroupID(uid); + } else if (name.equals("gid")) { + uint32_t gid = UINT32_MAX; + value.getAsInteger(0, gid); + process_info.SetGroupID(gid); + } else if (name.equals("egid")) { + uint32_t gid = UINT32_MAX; + value.getAsInteger(0, gid); + process_info.SetEffectiveGroupID(gid); + } else if (name.equals("triple")) { + StringExtractor extractor(value); + std::string triple; + extractor.GetHexByteString(triple); + process_info.GetArchitecture().SetTriple(triple.c_str()); + } else if (name.equals("name")) { + StringExtractor extractor(value); + // The process name from ASCII hex bytes since we can't + // control the characters in a process name std::string name; - std::string value; - StringExtractor extractor; - - uint32_t cpu = LLDB_INVALID_CPUTYPE; - uint32_t sub = 0; - std::string vendor; - std::string os_type; - - while (response.GetNameColonValue(name, value)) - { - if (name.compare("pid") == 0) - { - process_info.SetProcessID (StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); - } - else if (name.compare("ppid") == 0) - { - process_info.SetParentProcessID (StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); - } - else if (name.compare("uid") == 0) - { - process_info.SetUserID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); - } - else if (name.compare("euid") == 0) - { - process_info.SetEffectiveUserID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); - } - else if (name.compare("gid") == 0) - { - process_info.SetGroupID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); - } - else if (name.compare("egid") == 0) - { - process_info.SetEffectiveGroupID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); - } - else if (name.compare("triple") == 0) - { - StringExtractor extractor; - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (value); - process_info.GetArchitecture ().SetTriple (value.c_str()); - } - else if (name.compare("name") == 0) - { - StringExtractor extractor; - // The process name from ASCII hex bytes since we can't - // control the characters in a process name - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (value); - process_info.GetExecutableFile().SetFile (value.c_str(), false); - } - else if (name.compare("cputype") == 0) - { - cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16); - } - else if (name.compare("cpusubtype") == 0) - { - sub = StringConvert::ToUInt32 (value.c_str(), 0, 16); - } - else if (name.compare("vendor") == 0) - { - vendor = value; - } - else if (name.compare("ostype") == 0) - { - os_type = value; - } - } - - if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) - { - if (vendor == "apple") - { - process_info.GetArchitecture().SetArchitecture (eArchTypeMachO, cpu, sub); - process_info.GetArchitecture().GetTriple().setVendorName (llvm::StringRef (vendor)); - process_info.GetArchitecture().GetTriple().setOSName (llvm::StringRef (os_type)); - } - } - - if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) - return true; - } - return false; -} - -bool -GDBRemoteCommunicationClient::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) -{ - process_info.Clear(); - - if (m_supports_qProcessInfoPID) - { - char packet[32]; - const int packet_len = ::snprintf (packet, sizeof (packet), "qProcessInfoPID:%" PRIu64, pid); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - return DecodeProcessInfoResponse (response, process_info); - } - else - { - m_supports_qProcessInfoPID = false; - return false; - } - } - return false; -} - -bool -GDBRemoteCommunicationClient::GetCurrentProcessInfo (bool allow_lazy) -{ - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); - - if (allow_lazy) - { - if (m_qProcessInfo_is_valid == eLazyBoolYes) - return true; - if (m_qProcessInfo_is_valid == eLazyBoolNo) - return false; - } - - GetHostInfo (); - - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse ("qProcessInfo", response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - { - std::string name; - std::string value; - uint32_t cpu = LLDB_INVALID_CPUTYPE; - uint32_t sub = 0; - std::string arch_name; - std::string os_name; - std::string vendor_name; - std::string triple; - std::string elf_abi; - uint32_t pointer_byte_size = 0; - StringExtractor extractor; - ByteOrder byte_order = eByteOrderInvalid; - uint32_t num_keys_decoded = 0; - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - while (response.GetNameColonValue(name, value)) - { - if (name.compare("cputype") == 0) - { - cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16); - if (cpu != LLDB_INVALID_CPUTYPE) - ++num_keys_decoded; - } - else if (name.compare("cpusubtype") == 0) - { - sub = StringConvert::ToUInt32 (value.c_str(), 0, 16); - if (sub != 0) - ++num_keys_decoded; - } - else if (name.compare("triple") == 0) - { - StringExtractor extractor; - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString (triple); - ++num_keys_decoded; - } - else if (name.compare("ostype") == 0) - { - os_name.swap (value); - ++num_keys_decoded; - } - else if (name.compare("vendor") == 0) - { - vendor_name.swap(value); - ++num_keys_decoded; - } - else if (name.compare("endian") == 0) - { - ++num_keys_decoded; - if (value.compare("little") == 0) - byte_order = eByteOrderLittle; - else if (value.compare("big") == 0) - byte_order = eByteOrderBig; - else if (value.compare("pdp") == 0) - byte_order = eByteOrderPDP; - else - --num_keys_decoded; - } - else if (name.compare("ptrsize") == 0) - { - pointer_byte_size = StringConvert::ToUInt32 (value.c_str(), 0, 16); - if (pointer_byte_size != 0) - ++num_keys_decoded; - } - else if (name.compare("pid") == 0) - { - pid = StringConvert::ToUInt64(value.c_str(), 0, 16); - if (pid != LLDB_INVALID_PROCESS_ID) - ++num_keys_decoded; - } - else if (name.compare("elf_abi") == 0) - { - elf_abi = value; - ++num_keys_decoded; - } - } - if (num_keys_decoded > 0) - m_qProcessInfo_is_valid = eLazyBoolYes; - if (pid != LLDB_INVALID_PROCESS_ID) - { - m_curr_pid_is_valid = eLazyBoolYes; - m_curr_pid = pid; - } - - // Set the ArchSpec from the triple if we have it. - if (!triple.empty ()) - { - m_process_arch.SetTriple (triple.c_str ()); - m_process_arch.SetFlags(elf_abi); - if (pointer_byte_size) - { - assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); - } - } - else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) - { - llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); - - assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); - switch (triple.getObjectFormat()) { - case llvm::Triple::MachO: - m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); - break; - case llvm::Triple::ELF: - m_process_arch.SetArchitecture (eArchTypeELF, cpu, sub); - break; - case llvm::Triple::COFF: - m_process_arch.SetArchitecture (eArchTypeCOFF, cpu, sub); - break; - case llvm::Triple::UnknownObjectFormat: - if (log) - log->Printf("error: failed to determine target architecture"); - return false; - } - - if (pointer_byte_size) - { - assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); - } - if (byte_order != eByteOrderInvalid) - { - assert (byte_order == m_process_arch.GetByteOrder()); - } - m_process_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); - m_process_arch.GetTriple().setOSName(llvm::StringRef (os_name)); - m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); - m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); - } - return true; - } - } - else - { - m_qProcessInfo_is_valid = eLazyBoolNo; - } - - return false; -} - - -uint32_t -GDBRemoteCommunicationClient::FindProcesses (const ProcessInstanceInfoMatch &match_info, - ProcessInstanceInfoList &process_infos) -{ - process_infos.Clear(); - - if (m_supports_qfProcessInfo) - { - StreamString packet; - packet.PutCString ("qfProcessInfo"); - if (!match_info.MatchAllProcesses()) - { - packet.PutChar (':'); - const char *name = match_info.GetProcessInfo().GetName(); - bool has_name_match = false; - if (name && name[0]) - { - has_name_match = true; - NameMatchType name_match_type = match_info.GetNameMatchType(); - switch (name_match_type) - { - case eNameMatchIgnore: - has_name_match = false; - break; - - case eNameMatchEquals: - packet.PutCString ("name_match:equals;"); - break; - - case eNameMatchContains: - packet.PutCString ("name_match:contains;"); - break; - - case eNameMatchStartsWith: - packet.PutCString ("name_match:starts_with;"); - break; - - case eNameMatchEndsWith: - packet.PutCString ("name_match:ends_with;"); - break; - - case eNameMatchRegularExpression: - packet.PutCString ("name_match:regex;"); - break; - } - if (has_name_match) - { - packet.PutCString ("name:"); - packet.PutBytesAsRawHex8(name, ::strlen(name)); - packet.PutChar (';'); - } - } - - if (match_info.GetProcessInfo().ProcessIDIsValid()) - packet.Printf("pid:%" PRIu64 ";",match_info.GetProcessInfo().GetProcessID()); - if (match_info.GetProcessInfo().ParentProcessIDIsValid()) - packet.Printf("parent_pid:%" PRIu64 ";",match_info.GetProcessInfo().GetParentProcessID()); - if (match_info.GetProcessInfo().UserIDIsValid()) - packet.Printf("uid:%u;",match_info.GetProcessInfo().GetUserID()); - if (match_info.GetProcessInfo().GroupIDIsValid()) - packet.Printf("gid:%u;",match_info.GetProcessInfo().GetGroupID()); - if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) - packet.Printf("euid:%u;",match_info.GetProcessInfo().GetEffectiveUserID()); - if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) - packet.Printf("egid:%u;",match_info.GetProcessInfo().GetEffectiveGroupID()); - if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) - packet.Printf("all_users:%u;",match_info.GetMatchAllUsers() ? 1 : 0); - if (match_info.GetProcessInfo().GetArchitecture().IsValid()) - { - const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture(); - const llvm::Triple &triple = match_arch.GetTriple(); - packet.PutCString("triple:"); - packet.PutCString(triple.getTriple().c_str()); - packet.PutChar (';'); - } - } - StringExtractorGDBRemote response; - // Increase timeout as the first qfProcessInfo packet takes a long time - // on Android. The value of 1min was arrived at empirically. - GDBRemoteCommunication::ScopedTimeout timeout (*this, 60); - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) - { - do - { - ProcessInstanceInfo process_info; - if (!DecodeProcessInfoResponse (response, process_info)) - break; - process_infos.Append(process_info); - response.GetStringRef().clear(); - response.SetFilePos(0); - } while (SendPacketAndWaitForResponse ("qsProcessInfo", strlen ("qsProcessInfo"), response, false) == PacketResult::Success); - } - else - { - m_supports_qfProcessInfo = false; - return 0; - } - } - return process_infos.GetSize(); - -} - -bool -GDBRemoteCommunicationClient::GetUserName (uint32_t uid, std::string &name) -{ - if (m_supports_qUserName) - { - char packet[32]; - const int packet_len = ::snprintf (packet, sizeof (packet), "qUserName:%i", uid); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - { - // Make sure we parsed the right number of characters. The response is - // the hex encoded user name and should make up the entire packet. - // If there are any non-hex ASCII bytes, the length won't match below.. - if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) - return true; - } - } - else - { - m_supports_qUserName = false; - return false; - } - } - return false; - -} - -bool -GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name) -{ - if (m_supports_qGroupName) - { - char packet[32]; - const int packet_len = ::snprintf (packet, sizeof (packet), "qGroupName:%i", gid); - assert (packet_len < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - { - // Make sure we parsed the right number of characters. The response is - // the hex encoded group name and should make up the entire packet. - // If there are any non-hex ASCII bytes, the length won't match below.. - if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) - return true; - } - } - else - { - m_supports_qGroupName = false; - return false; - } - } - return false; -} - -bool -GDBRemoteCommunicationClient::SetNonStopMode (const bool enable) -{ - // Form non-stop packet request + extractor.GetHexByteString(name); + process_info.GetExecutableFile().SetFile(name, false); + } else if (name.equals("cputype")) { + value.getAsInteger(0, cpu); + } else if (name.equals("cpusubtype")) { + value.getAsInteger(0, sub); + } else if (name.equals("vendor")) { + vendor = value; + } else if (name.equals("ostype")) { + os_type = value; + } + } + + if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { + if (vendor == "apple") { + process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, + sub); + process_info.GetArchitecture().GetTriple().setVendorName( + llvm::StringRef(vendor)); + process_info.GetArchitecture().GetTriple().setOSName( + llvm::StringRef(os_type)); + } + } + + if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + } + return false; +} + +bool GDBRemoteCommunicationClient::GetProcessInfo( + lldb::pid_t pid, ProcessInstanceInfo &process_info) { + process_info.Clear(); + + if (m_supports_qProcessInfoPID) { char packet[32]; - const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); + const int packet_len = + ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid); assert(packet_len < (int)sizeof(packet)); - + UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; - // Send to target - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - if (response.IsOKResponse()) - return true; - - // Failed or not supported - return false; - -} - -static void -MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) -{ - packet.Clear(); - packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); - uint32_t bytes_left = send_size; - while (bytes_left > 0) - { - if (bytes_left >= 26) - { - packet.PutCString("abcdefghijklmnopqrstuvwxyz"); - bytes_left -= 26; - } - else - { - packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); - bytes_left = 0; - } - } -} - -template<typename T> -T calculate_standard_deviation(const std::vector<T> &v) -{ - T sum = std::accumulate(std::begin(v), std::end(v), T(0)); - T mean = sum / (T)v.size(); - T accum = T(0); - std::for_each (std::begin(v), std::end(v), [&](const T d) { - T delta = d - mean; - accum += delta * delta; - }); - - T stdev = sqrt(accum / (v.size()-1)); - return stdev; -} - -void -GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, bool json, Stream &strm) -{ - uint32_t i; - TimeValue start_time, end_time; - uint64_t total_time_nsec; - if (SendSpeedTestPacket (0, 0)) - { - StreamString packet; - if (json) - strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n \"results\" : [", num_packets); - else - strm.Printf("Testing sending %u packets of various sizes:\n", num_packets); - strm.Flush(); - - uint32_t result_idx = 0; - uint32_t send_size; - std::vector<float> packet_times; - - for (send_size = 0; send_size <= max_send; send_size ? send_size *= 2 : send_size = 4) - { - for (uint32_t recv_size = 0; recv_size <= max_recv; recv_size ? recv_size *= 2 : recv_size = 4) - { - MakeSpeedTestPacket (packet, send_size, recv_size); - - packet_times.clear(); - // Test how long it takes to send 'num_packets' packets - start_time = TimeValue::Now(); - for (i=0; i<num_packets; ++i) - { - TimeValue packet_start_time = TimeValue::Now(); - StringExtractorGDBRemote response; - SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); - TimeValue packet_end_time = TimeValue::Now(); - uint64_t packet_time_nsec = packet_end_time.GetAsNanoSecondsSinceJan1_1970() - packet_start_time.GetAsNanoSecondsSinceJan1_1970(); - packet_times.push_back((float)packet_time_nsec); - } - end_time = TimeValue::Now(); - total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); - - float packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; - float total_ms = (float)total_time_nsec/(float)TimeValue::NanoSecPerMilliSec; - float average_ms_per_packet = total_ms / num_packets; - const float standard_deviation = calculate_standard_deviation<float>(packet_times); - if (json) - { - strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 ", \"standard_deviation_nsec\" : %9" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec, (uint64_t)standard_deviation); - ++result_idx; - } - else - { - strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) in %" PRIu64 ".%9.9" PRIu64 " sec for %9.2f packets/sec (%10.6f ms per packet) with standard deviation of %10.6f ms\n", - send_size, - recv_size, - total_time_nsec / TimeValue::NanoSecPerSec, - total_time_nsec % TimeValue::NanoSecPerSec, - packets_per_second, - average_ms_per_packet, - standard_deviation/(float)TimeValue::NanoSecPerMilliSec); - } - strm.Flush(); - } - } - - const uint64_t k_recv_amount = 4*1024*1024; // Receive amount in bytes - - const float k_recv_amount_mb = (float)k_recv_amount/(1024.0f*1024.0f); - if (json) - strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" : %" PRIu64 ",\n \"results\" : [", k_recv_amount); - else - strm.Printf("Testing receiving %2.1fMB of data using varying receive packet sizes:\n", k_recv_amount_mb); - strm.Flush(); - send_size = 0; - result_idx = 0; - for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) - { - MakeSpeedTestPacket (packet, send_size, recv_size); - - // If we have a receive size, test how long it takes to receive 4MB of data - if (recv_size > 0) - { - start_time = TimeValue::Now(); - uint32_t bytes_read = 0; - uint32_t packet_count = 0; - while (bytes_read < k_recv_amount) - { - StringExtractorGDBRemote response; - SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); - bytes_read += recv_size; - ++packet_count; - } - end_time = TimeValue::Now(); - total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); - float mb_second = ((((float)k_recv_amount)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec) / (1024.0*1024.0); - float packets_per_second = (((float)packet_count)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; - float total_ms = (float)total_time_nsec/(float)TimeValue::NanoSecPerMilliSec; - float average_ms_per_packet = total_ms / packet_count; - - if (json) - { - strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec); - ++result_idx; - } - else - { - strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) %6u packets needed to receive %2.1fMB in %" PRIu64 ".%9.9" PRIu64 " sec for %f MB/sec for %9.2f packets/sec (%10.6f ms per packet)\n", - send_size, - recv_size, - packet_count, - k_recv_amount_mb, - total_time_nsec / TimeValue::NanoSecPerSec, - total_time_nsec % TimeValue::NanoSecPerSec, - mb_second, - packets_per_second, - average_ms_per_packet); - } - strm.Flush(); - } - } - if (json) - strm.Printf("\n ]\n }\n}\n"); - else - strm.EOL(); - } -} - -bool -GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t recv_size) -{ + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + return DecodeProcessInfoResponse(response, process_info); + } else { + m_supports_qProcessInfoPID = false; + return false; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { + Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | + GDBR_LOG_PACKETS)); + + if (allow_lazy) { + if (m_qProcessInfo_is_valid == eLazyBoolYes) + return true; + if (m_qProcessInfo_is_valid == eLazyBoolNo) + return false; + } + + GetHostInfo(); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qProcessInfo", response, false) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string vendor_name; + std::string triple; + std::string elf_abi; + uint32_t pointer_byte_size = 0; + StringExtractor extractor; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + while (response.GetNameColonValue(name, value)) { + if (name.equals("cputype")) { + if (!value.getAsInteger(16, cpu)) + ++num_keys_decoded; + } else if (name.equals("cpusubtype")) { + if (!value.getAsInteger(16, sub)) + ++num_keys_decoded; + } else if (name.equals("triple")) { + StringExtractor extractor(value); + extractor.GetHexByteString(triple); + ++num_keys_decoded; + } else if (name.equals("ostype")) { + os_name = value; + ++num_keys_decoded; + } else if (name.equals("vendor")) { + vendor_name = value; + ++num_keys_decoded; + } else if (name.equals("endian")) { + byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) + .Case("little", eByteOrderLittle) + .Case("big", eByteOrderBig) + .Case("pdp", eByteOrderPDP) + .Default(eByteOrderInvalid); + if (byte_order != eByteOrderInvalid) + ++num_keys_decoded; + } else if (name.equals("ptrsize")) { + if (!value.getAsInteger(16, pointer_byte_size)) + ++num_keys_decoded; + } else if (name.equals("pid")) { + if (!value.getAsInteger(16, pid)) + ++num_keys_decoded; + } else if (name.equals("elf_abi")) { + elf_abi = value; + ++num_keys_decoded; + } + } + if (num_keys_decoded > 0) + m_qProcessInfo_is_valid = eLazyBoolYes; + if (pid != LLDB_INVALID_PROCESS_ID) { + m_curr_pid_is_valid = eLazyBoolYes; + m_curr_pid = pid; + } + + // Set the ArchSpec from the triple if we have it. + if (!triple.empty()) { + m_process_arch.SetTriple(triple.c_str()); + m_process_arch.SetFlags(elf_abi); + if (pointer_byte_size) { + assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); + } + } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && + !vendor_name.empty()) { + llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); + + assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); + switch (triple.getObjectFormat()) { + case llvm::Triple::MachO: + m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub); + break; + case llvm::Triple::ELF: + m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub); + break; + case llvm::Triple::COFF: + m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub); + break; + case llvm::Triple::UnknownObjectFormat: + if (log) + log->Printf("error: failed to determine target architecture"); + return false; + } + + if (pointer_byte_size) { + assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_process_arch.GetByteOrder()); + } + m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); + m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name)); + m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); + m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); + } + return true; + } + } else { + m_qProcessInfo_is_valid = eLazyBoolNo; + } + + return false; +} + +uint32_t GDBRemoteCommunicationClient::FindProcesses( + const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + process_infos.Clear(); + + if (m_supports_qfProcessInfo) { StreamString packet; - packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); - uint32_t bytes_left = send_size; - while (bytes_left > 0) - { - if (bytes_left >= 26) - { - packet.PutCString("abcdefghijklmnopqrstuvwxyz"); - bytes_left -= 26; - } - else - { - packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); - bytes_left = 0; - } + packet.PutCString("qfProcessInfo"); + if (!match_info.MatchAllProcesses()) { + packet.PutChar(':'); + const char *name = match_info.GetProcessInfo().GetName(); + bool has_name_match = false; + if (name && name[0]) { + has_name_match = true; + NameMatchType name_match_type = match_info.GetNameMatchType(); + switch (name_match_type) { + case eNameMatchIgnore: + has_name_match = false; + break; + + case eNameMatchEquals: + packet.PutCString("name_match:equals;"); + break; + + case eNameMatchContains: + packet.PutCString("name_match:contains;"); + break; + + case eNameMatchStartsWith: + packet.PutCString("name_match:starts_with;"); + break; + + case eNameMatchEndsWith: + packet.PutCString("name_match:ends_with;"); + break; + + case eNameMatchRegularExpression: + packet.PutCString("name_match:regex;"); + break; + } + if (has_name_match) { + packet.PutCString("name:"); + packet.PutBytesAsRawHex8(name, ::strlen(name)); + packet.PutChar(';'); + } + } + + if (match_info.GetProcessInfo().ProcessIDIsValid()) + packet.Printf("pid:%" PRIu64 ";", + match_info.GetProcessInfo().GetProcessID()); + if (match_info.GetProcessInfo().ParentProcessIDIsValid()) + packet.Printf("parent_pid:%" PRIu64 ";", + match_info.GetProcessInfo().GetParentProcessID()); + if (match_info.GetProcessInfo().UserIDIsValid()) + packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID()); + if (match_info.GetProcessInfo().GroupIDIsValid()) + packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID()); + if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) + packet.Printf("euid:%u;", + match_info.GetProcessInfo().GetEffectiveUserID()); + if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) + packet.Printf("egid:%u;", + match_info.GetProcessInfo().GetEffectiveGroupID()); + if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) + packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0); + if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { + const ArchSpec &match_arch = + match_info.GetProcessInfo().GetArchitecture(); + const llvm::Triple &triple = match_arch.GetTriple(); + packet.PutCString("triple:"); + packet.PutCString(triple.getTriple()); + packet.PutChar(';'); + } } - StringExtractorGDBRemote response; - return SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success; -} - -bool -GDBRemoteCommunicationClient::LaunchGDBServer (const char *remote_accept_hostname, - lldb::pid_t &pid, - uint16_t &port, - std::string &socket_name) -{ - pid = LLDB_INVALID_PROCESS_ID; - port = 0; - socket_name.clear(); - + // Increase timeout as the first qfProcessInfo packet takes a long time + // on Android. The value of 1min was arrived at empirically. + ScopedTimeout timeout(*this, minutes(1)); + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success) { + do { + ProcessInstanceInfo process_info; + if (!DecodeProcessInfoResponse(response, process_info)) + break; + process_infos.Append(process_info); + response.GetStringRef().clear(); + response.SetFilePos(0); + } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) == + PacketResult::Success); + } else { + m_supports_qfProcessInfo = false; + return 0; + } + } + return process_infos.GetSize(); +} + +bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, + std::string &name) { + if (m_supports_qUserName) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qUserName:%i", uid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; - StreamString stream; - stream.PutCString("qLaunchGDBServer;"); - std::string hostname; - if (remote_accept_hostname && remote_accept_hostname[0]) - hostname = remote_accept_hostname; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + // Make sure we parsed the right number of characters. The response is + // the hex encoded user name and should make up the entire packet. + // If there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString(name) * 2 == + response.GetStringRef().size()) + return true; + } + } else { + m_supports_qUserName = false; + return false; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, + std::string &name) { + if (m_supports_qGroupName) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + // Make sure we parsed the right number of characters. The response is + // the hex encoded group name and should make up the entire packet. + // If there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString(name) * 2 == + response.GetStringRef().size()) + return true; + } + } else { + m_supports_qGroupName = false; + return false; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) { + // Form non-stop packet request + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + + StringExtractorGDBRemote response; + // Send to target + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) + if (response.IsOKResponse()) + return true; + + // Failed or not supported + return false; +} + +static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, + uint32_t recv_size) { + packet.Clear(); + packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) { + if (bytes_left >= 26) { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } else { + packet.Printf("%*.*s;", bytes_left, bytes_left, + "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } +} + +duration<float> +calculate_standard_deviation(const std::vector<duration<float>> &v) { + using Dur = duration<float>; + Dur sum = std::accumulate(std::begin(v), std::end(v), Dur()); + Dur mean = sum / v.size(); + float accum = 0; + for (auto d : v) { + float delta = (d - mean).count(); + accum += delta * delta; + }; + + return Dur(sqrtf(accum / (v.size() - 1))); +} + +void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets, + uint32_t max_send, + uint32_t max_recv, + uint64_t recv_amount, + bool json, Stream &strm) { + uint32_t i; + if (SendSpeedTestPacket(0, 0)) { + StreamString packet; + if (json) + strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n " + "\"results\" : [", + num_packets); else - { - if (HostInfo::GetHostname(hostname)) - { - // Make the GDB server we launch only accept connections from this host - stream.Printf("host:%s;", hostname.c_str()); - } - else - { - // Make the GDB server we launch accept connections from any host since we can't figure out the hostname - stream.Printf("host:*;"); + strm.Printf("Testing sending %u packets of various sizes:\n", + num_packets); + strm.Flush(); + + uint32_t result_idx = 0; + uint32_t send_size; + std::vector<duration<float>> packet_times; + + for (send_size = 0; send_size <= max_send; + send_size ? send_size *= 2 : send_size = 4) { + for (uint32_t recv_size = 0; recv_size <= max_recv; + recv_size ? recv_size *= 2 : recv_size = 4) { + MakeSpeedTestPacket(packet, send_size, recv_size); + + packet_times.clear(); + // Test how long it takes to send 'num_packets' packets + const auto start_time = steady_clock::now(); + for (i = 0; i < num_packets; ++i) { + const auto packet_start_time = steady_clock::now(); + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse(packet.GetString(), response, false); + const auto packet_end_time = steady_clock::now(); + packet_times.push_back(packet_end_time - packet_start_time); + } + const auto end_time = steady_clock::now(); + const auto total_time = end_time - start_time; + + float packets_per_second = + ((float)num_packets) / duration<float>(total_time).count(); + auto average_per_packet = total_time / num_packets; + const duration<float> standard_deviation = + calculate_standard_deviation(packet_times); + if (json) { + strm.Printf("%s\n {\"send_size\" : %6" PRIu32 + ", \"recv_size\" : %6" PRIu32 + ", \"total_time_nsec\" : %12" PRIu64 + ", \"standard_deviation_nsec\" : %9" PRIu64 " }", + result_idx > 0 ? "," : "", send_size, recv_size, + duration_cast<nanoseconds>(total_time).count(), + duration_cast<nanoseconds>(standard_deviation).count()); + ++result_idx; + } else { + strm.Printf( + "qSpeedTest(send=%-7u, recv=%-7u) in %.9f" + " sec for %9.2f packets/sec (%10.6f ms per packet) with standard " + "deviation of %10.6f ms\n", + send_size, recv_size, duration<float>(total_time).count(), + packets_per_second, + duration<float, std::milli>(average_per_packet).count(), + duration<float, std::milli>(standard_deviation).count()); } + strm.Flush(); + } } - const char *packet = stream.GetData(); - int packet_len = stream.GetSize(); - - // give the process a few seconds to startup - GDBRemoteCommunication::ScopedTimeout timeout (*this, 10); - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - std::string name; - std::string value; - StringExtractor extractor; - while (response.GetNameColonValue(name, value)) - { - if (name.compare("port") == 0) - port = StringConvert::ToUInt32(value.c_str(), 0, 0); - else if (name.compare("pid") == 0) - pid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); - else if (name.compare("socket_name") == 0) - { - extractor.GetStringRef().swap(value); - extractor.SetFilePos(0); - extractor.GetHexByteString(value); - - socket_name = value; - } + const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f); + if (json) + strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" " + ": %" PRIu64 ",\n \"results\" : [", + recv_amount); + else + strm.Printf("Testing receiving %2.1fMB of data using varying receive " + "packet sizes:\n", + k_recv_amount_mb); + strm.Flush(); + send_size = 0; + result_idx = 0; + for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { + MakeSpeedTestPacket(packet, send_size, recv_size); + + // If we have a receive size, test how long it takes to receive 4MB of + // data + if (recv_size > 0) { + const auto start_time = steady_clock::now(); + uint32_t bytes_read = 0; + uint32_t packet_count = 0; + while (bytes_read < recv_amount) { + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse(packet.GetString(), response, false); + bytes_read += recv_size; + ++packet_count; + } + const auto end_time = steady_clock::now(); + const auto total_time = end_time - start_time; + float mb_second = ((float)recv_amount) / + duration<float>(total_time).count() / + (1024.0 * 1024.0); + float packets_per_second = + ((float)packet_count) / duration<float>(total_time).count(); + const auto average_per_packet = total_time / packet_count; + + if (json) { + strm.Printf("%s\n {\"send_size\" : %6" PRIu32 + ", \"recv_size\" : %6" PRIu32 + ", \"total_time_nsec\" : %12" PRIu64 " }", + result_idx > 0 ? "," : "", send_size, recv_size, + duration_cast<nanoseconds>(total_time).count()); + ++result_idx; + } else { + strm.Printf("qSpeedTest(send=%-7u, recv=%-7u) %6u packets needed to " + "receive %2.1fMB in %.9f" + " sec for %f MB/sec for %9.2f packets/sec (%10.6f ms per " + "packet)\n", + send_size, recv_size, packet_count, k_recv_amount_mb, + duration<float>(total_time).count(), mb_second, + packets_per_second, + duration<float, std::milli>(average_per_packet).count()); } - return true; + strm.Flush(); + } } - return false; + if (json) + strm.Printf("\n ]\n }\n}\n"); + else + strm.EOL(); + } +} + +bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size, + uint32_t recv_size) { + StreamString packet; + packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) { + if (bytes_left >= 26) { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } else { + packet.Printf("%*.*s;", bytes_left, bytes_left, + "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } + + StringExtractorGDBRemote response; + return SendPacketAndWaitForResponse(packet.GetString(), response, false) == + PacketResult::Success; +} + +bool GDBRemoteCommunicationClient::LaunchGDBServer( + const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, + std::string &socket_name) { + pid = LLDB_INVALID_PROCESS_ID; + port = 0; + socket_name.clear(); + + StringExtractorGDBRemote response; + StreamString stream; + stream.PutCString("qLaunchGDBServer;"); + std::string hostname; + if (remote_accept_hostname && remote_accept_hostname[0]) + hostname = remote_accept_hostname; + else { + if (HostInfo::GetHostname(hostname)) { + // Make the GDB server we launch only accept connections from this host + stream.Printf("host:%s;", hostname.c_str()); + } else { + // Make the GDB server we launch accept connections from any host since we + // can't figure out the hostname + stream.Printf("host:*;"); + } + } + // give the process a few seconds to startup + ScopedTimeout timeout(*this, seconds(10)); + + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + llvm::StringRef name; + llvm::StringRef value; + while (response.GetNameColonValue(name, value)) { + if (name.equals("port")) + value.getAsInteger(0, port); + else if (name.equals("pid")) + value.getAsInteger(0, pid); + else if (name.compare("socket_name") == 0) { + StringExtractor extractor(value); + extractor.GetHexByteString(socket_name); + } + } + return true; + } + return false; } -size_t -GDBRemoteCommunicationClient::QueryGDBServer (std::vector<std::pair<uint16_t, std::string>>& connection_urls) -{ - connection_urls.clear(); +size_t GDBRemoteCommunicationClient::QueryGDBServer( + std::vector<std::pair<uint16_t, std::string>> &connection_urls) { + connection_urls.clear(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != PacketResult::Success) - return 0; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != + PacketResult::Success) + return 0; - StructuredData::ObjectSP data = StructuredData::ParseJSON(response.GetStringRef()); - if (!data) - return 0; + StructuredData::ObjectSP data = + StructuredData::ParseJSON(response.GetStringRef()); + if (!data) + return 0; - StructuredData::Array* array = data->GetAsArray(); - if (!array) - return 0; + StructuredData::Array *array = data->GetAsArray(); + if (!array) + return 0; - for (size_t i = 0, count = array->GetSize(); i < count; ++i) - { - StructuredData::Dictionary* element = nullptr; - if (!array->GetItemAtIndexAsDictionary(i, element)) - continue; + for (size_t i = 0, count = array->GetSize(); i < count; ++i) { + StructuredData::Dictionary *element = nullptr; + if (!array->GetItemAtIndexAsDictionary(i, element)) + continue; - uint16_t port = 0; - if (StructuredData::ObjectSP port_osp = element->GetValueForKey(llvm::StringRef("port"))) - port = port_osp->GetIntegerValue(0); + uint16_t port = 0; + if (StructuredData::ObjectSP port_osp = + element->GetValueForKey(llvm::StringRef("port"))) + port = port_osp->GetIntegerValue(0); - std::string socket_name; - if (StructuredData::ObjectSP socket_name_osp = element->GetValueForKey(llvm::StringRef("socket_name"))) - socket_name = socket_name_osp->GetStringValue(); + std::string socket_name; + if (StructuredData::ObjectSP socket_name_osp = + element->GetValueForKey(llvm::StringRef("socket_name"))) + socket_name = socket_name_osp->GetStringValue(); - if (port != 0 || !socket_name.empty()) - connection_urls.emplace_back(port, socket_name); - } - return connection_urls.size(); + if (port != 0 || !socket_name.empty()) + connection_urls.emplace_back(port, socket_name); + } + return connection_urls.size(); } -bool -GDBRemoteCommunicationClient::KillSpawnedProcess (lldb::pid_t pid) -{ - StreamString stream; - stream.Printf ("qKillSpawnedProcess:%" PRId64 , pid); - const char *packet = stream.GetData(); - int packet_len = stream.GetSize(); +bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) { + StreamString stream; + stream.Printf("qKillSpawnedProcess:%" PRId64, pid); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - return true; - } - return false; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) + return true; + } + return false; } -bool -GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid) -{ - if (m_curr_tid == tid) - return true; - - char packet[32]; - int packet_len; - if (tid == UINT64_MAX) - packet_len = ::snprintf (packet, sizeof(packet), "Hg-1"); - else - packet_len = ::snprintf (packet, sizeof(packet), "Hg%" PRIx64, tid); - assert (packet_len + 1 < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - m_curr_tid = tid; - return true; - } +bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) { + if (m_curr_tid == tid) + return true; - /* - * Connected bare-iron target (like YAMON gdb-stub) may not have support for Hg packet. - * The reply from '?' packet could be as simple as 'S05'. There is no packet which can - * give us pid and/or tid. Assume pid=tid=1 in such cases. - */ - if (response.IsUnsupportedResponse() && IsConnected()) - { - m_curr_tid = 1; - return true; - } - } - return false; -} + char packet[32]; + int packet_len; + if (tid == UINT64_MAX) + packet_len = ::snprintf(packet, sizeof(packet), "Hg-1"); + else + packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid); + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_curr_tid = tid; + return true; + } + + /* + * Connected bare-iron target (like YAMON gdb-stub) may not have support for + * Hg packet. + * The reply from '?' packet could be as simple as 'S05'. There is no packet + * which can + * give us pid and/or tid. Assume pid=tid=1 in such cases. + */ + if (response.IsUnsupportedResponse() && IsConnected()) { + m_curr_tid = 1; + return true; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) { + if (m_curr_tid_run == tid) + return true; -bool -GDBRemoteCommunicationClient::SetCurrentThreadForRun (uint64_t tid) -{ - if (m_curr_tid_run == tid) + char packet[32]; + int packet_len; + if (tid == UINT64_MAX) + packet_len = ::snprintf(packet, sizeof(packet), "Hc-1"); + else + packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid); + + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_curr_tid_run = tid; + return true; + } + + /* + * Connected bare-iron target (like YAMON gdb-stub) may not have support for + * Hc packet. + * The reply from '?' packet could be as simple as 'S05'. There is no packet + * which can + * give us pid and/or tid. Assume pid=tid=1 in such cases. + */ + if (response.IsUnsupportedResponse() && IsConnected()) { + m_curr_tid_run = 1; + return true; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetStopReply( + StringExtractorGDBRemote &response) { + if (SendPacketAndWaitForResponse("?", response, false) == + PacketResult::Success) + return response.IsNormalResponse(); + return false; +} + +bool GDBRemoteCommunicationClient::GetThreadStopInfo( + lldb::tid_t tid, StringExtractorGDBRemote &response) { + if (m_supports_qThreadStopInfo) { + char packet[256]; + int packet_len = + ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + if (SendPacketAndWaitForResponse(packet, response, false) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_qThreadStopInfo = false; + else if (response.IsNormalResponse()) return true; - - char packet[32]; - int packet_len; - if (tid == UINT64_MAX) - packet_len = ::snprintf (packet, sizeof(packet), "Hc-1"); - else - packet_len = ::snprintf (packet, sizeof(packet), "Hc%" PRIx64, tid); - - assert (packet_len + 1 < (int)sizeof(packet)); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - m_curr_tid_run = tid; - return true; - } - - /* - * Connected bare-iron target (like YAMON gdb-stub) may not have support for Hc packet. - * The reply from '?' packet could be as simple as 'S05'. There is no packet which can - * give us pid and/or tid. Assume pid=tid=1 in such cases. - */ - if (response.IsUnsupportedResponse() && IsConnected()) - { - m_curr_tid_run = 1; - return true; - } + else + return false; + } else { + m_supports_qThreadStopInfo = false; } - return false; + } + return false; } -bool -GDBRemoteCommunicationClient::GetStopReply (StringExtractorGDBRemote &response) -{ - if (SendPacketAndWaitForResponse("?", 1, response, false) == PacketResult::Success) - return response.IsNormalResponse(); - return false; -} +uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( + GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, + __FUNCTION__, insert ? "add" : "remove", addr); -bool -GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtractorGDBRemote &response) -{ - if (m_supports_qThreadStopInfo) - { - char packet[256]; - int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); - assert (packet_len < (int)sizeof(packet)); - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - m_supports_qThreadStopInfo = false; - else if (response.IsNormalResponse()) - return true; - else - return false; - } - else - { - m_supports_qThreadStopInfo = false; - } + // Check if the stub is known not to support this breakpoint type + if (!SupportsGDBStoppointPacket(type)) + return UINT8_MAX; + // Construct the breakpoint packet + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", + insert ? 'Z' : 'z', type, addr, length); + // Check we haven't overwritten the end of the packet buffer + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + // Make sure the response is either "OK", "EXX" where XX are two hex digits, + // or "" (unsupported) + response.SetResponseValidatorToOKErrorNotSupported(); + // Try to send the breakpoint packet, and check that it was correctly sent + if (SendPacketAndWaitForResponse(packet, response, true) == + PacketResult::Success) { + // Receive and OK packet when the breakpoint successfully placed + if (response.IsOKResponse()) + return 0; + + // Error while setting breakpoint, send back specific error + if (response.IsErrorResponse()) + return response.GetError(); + + // Empty packet informs us that breakpoint is not supported + if (response.IsUnsupportedResponse()) { + // Disable this breakpoint type since it is unsupported + switch (type) { + case eBreakpointSoftware: + m_supports_z0 = false; + break; + case eBreakpointHardware: + m_supports_z1 = false; + break; + case eWatchpointWrite: + m_supports_z2 = false; + break; + case eWatchpointRead: + m_supports_z3 = false; + break; + case eWatchpointReadWrite: + m_supports_z4 = false; + break; + case eStoppointInvalid: + return UINT8_MAX; + } } - return false; + } + // Signal generic failure + return UINT8_MAX; } +size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs( + std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) { + thread_ids.clear(); -uint8_t -GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, bool insert, addr_t addr, uint32_t length) -{ - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); - if (log) - log->Printf ("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, - __FUNCTION__, insert ? "add" : "remove", addr); - - // Check if the stub is known not to support this breakpoint type - if (!SupportsGDBStoppointPacket(type)) - return UINT8_MAX; - // Construct the breakpoint packet - char packet[64]; - const int packet_len = ::snprintf (packet, - sizeof(packet), - "%c%i,%" PRIx64 ",%x", - insert ? 'Z' : 'z', - type, - addr, - length); - // Check we haven't overwritten the end of the packet buffer - assert (packet_len + 1 < (int)sizeof(packet)); + Lock lock(*this, false); + if (lock) { + sequence_mutex_unavailable = false; StringExtractorGDBRemote response; - // Make sure the response is either "OK", "EXX" where XX are two hex digits, or "" (unsupported) - response.SetResponseValidatorToOKErrorNotSupported(); - // Try to send the breakpoint packet, and check that it was correctly sent - if (SendPacketAndWaitForResponse(packet, packet_len, response, true) == PacketResult::Success) - { - // Receive and OK packet when the breakpoint successfully placed - if (response.IsOKResponse()) - return 0; - - // Error while setting breakpoint, send back specific error - if (response.IsErrorResponse()) - return response.GetError(); - - // Empty packet informs us that breakpoint is not supported - if (response.IsUnsupportedResponse()) - { - // Disable this breakpoint type since it is unsupported - switch (type) - { - case eBreakpointSoftware: m_supports_z0 = false; break; - case eBreakpointHardware: m_supports_z1 = false; break; - case eWatchpointWrite: m_supports_z2 = false; break; - case eWatchpointRead: m_supports_z3 = false; break; - case eWatchpointReadWrite: m_supports_z4 = false; break; - case eStoppointInvalid: return UINT8_MAX; - } - } - } - // Signal generic failure - return UINT8_MAX; -} - -size_t -GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids, - bool &sequence_mutex_unavailable) -{ - Mutex::Locker locker; - thread_ids.clear(); - - if (GetSequenceMutex (locker, "ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex")) - { - sequence_mutex_unavailable = false; - StringExtractorGDBRemote response; - - PacketResult packet_result; - for (packet_result = SendPacketAndWaitForResponseNoLock ("qfThreadInfo", strlen("qfThreadInfo"), response); - packet_result == PacketResult::Success && response.IsNormalResponse(); - packet_result = SendPacketAndWaitForResponseNoLock ("qsThreadInfo", strlen("qsThreadInfo"), response)) - { - char ch = response.GetChar(); - if (ch == 'l') - break; - if (ch == 'm') - { - do - { - tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); - - if (tid != LLDB_INVALID_THREAD_ID) - { - thread_ids.push_back (tid); - } - ch = response.GetChar(); // Skip the command separator - } while (ch == ','); // Make sure we got a comma separator - } - } - /* - * Connected bare-iron target (like YAMON gdb-stub) may not have support for - * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet could - * be as simple as 'S05'. There is no packet which can give us pid and/or tid. - * Assume pid=tid=1 in such cases. - */ - if (response.IsUnsupportedResponse() && thread_ids.size() == 0 && IsConnected()) - { - thread_ids.push_back (1); - } - } - else - { -#if defined (LLDB_CONFIGURATION_DEBUG) - // assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex"); + PacketResult packet_result; + for (packet_result = + SendPacketAndWaitForResponseNoLock("qfThreadInfo", response); + packet_result == PacketResult::Success && response.IsNormalResponse(); + packet_result = + SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) { + char ch = response.GetChar(); + if (ch == 'l') + break; + if (ch == 'm') { + do { + tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); + + if (tid != LLDB_INVALID_THREAD_ID) { + thread_ids.push_back(tid); + } + ch = response.GetChar(); // Skip the command separator + } while (ch == ','); // Make sure we got a comma separator + } + } + + /* + * Connected bare-iron target (like YAMON gdb-stub) may not have support for + * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet + * could + * be as simple as 'S05'. There is no packet which can give us pid and/or + * tid. + * Assume pid=tid=1 in such cases. + */ + if (response.IsUnsupportedResponse() && thread_ids.size() == 0 && + IsConnected()) { + thread_ids.push_back(1); + } + } else { +#if defined(LLDB_CONFIGURATION_DEBUG) +// assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the +// sequence mutex"); #else - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); - if (log) - log->Printf("error: failed to get packet sequence mutex, not sending packet 'qfThreadInfo'"); + Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | + GDBR_LOG_PACKETS)); + if (log) + log->Printf("error: failed to get packet sequence mutex, not sending " + "packet 'qfThreadInfo'"); #endif - sequence_mutex_unavailable = true; - } - return thread_ids.size(); + sequence_mutex_unavailable = true; + } + return thread_ids.size(); } -lldb::addr_t -GDBRemoteCommunicationClient::GetShlibInfoAddr() -{ - if (!IsRunning()) - { - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, false) == PacketResult::Success) - { - if (response.IsNormalResponse()) - return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); - } - } +lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) != + PacketResult::Success || + !response.IsNormalResponse()) return LLDB_INVALID_ADDRESS; -} - -lldb_private::Error -GDBRemoteCommunicationClient::RunShellCommand(const char *command, // Shouldn't be NULL - const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory - int *status_ptr, // Pass NULL if you don't want the process exit status - int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit - std::string *command_output, // Pass NULL if you don't want the command output - uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish -{ - lldb_private::StreamString stream; - stream.PutCString("qPlatform_shell:"); - stream.PutBytesAsRawHex8(command, strlen(command)); - stream.PutChar(','); - stream.PutHex32(timeout_sec); - if (working_dir) - { - std::string path{working_dir.GetPath(false)}; - stream.PutChar(','); - stream.PutCStringAsRawHex8(path.c_str()); - } - const char *packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - return Error("malformed reply"); - if (response.GetChar() != ',') - return Error("malformed reply"); - uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); - if (exitcode == UINT32_MAX) - return Error("unable to run remote process"); - else if (status_ptr) - *status_ptr = exitcode; - if (response.GetChar() != ',') - return Error("malformed reply"); - uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); - if (signo_ptr) - *signo_ptr = signo; - if (response.GetChar() != ',') - return Error("malformed reply"); - std::string output; - response.GetEscapedBinaryData(output); - if (command_output) - command_output->assign(output); - return Error(); - } - return Error("unable to send packet"); -} - -Error -GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, - uint32_t file_permissions) -{ - std::string path{file_spec.GetPath(false)}; - lldb_private::StreamString stream; - stream.PutCString("qPlatform_mkdir:"); - stream.PutHex32(file_permissions); + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); +} + +lldb_private::Error GDBRemoteCommunicationClient::RunShellCommand( + const char *command, // Shouldn't be NULL + const FileSpec & + working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass NULL if you don't want the command output + uint32_t + timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_shell:"); + stream.PutBytesAsRawHex8(command, strlen(command)); + stream.PutChar(','); + stream.PutHex32(timeout_sec); + if (working_dir) { + std::string path{working_dir.GetPath(false)}; stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); - const char *packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) - return Error("failed to send '%s' packet", packet); - + } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { if (response.GetChar() != 'F') - return Error("invalid response to '%s' packet", packet); - - return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); -} - -Error -GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, - uint32_t file_permissions) -{ - std::string path{file_spec.GetPath(false)}; - lldb_private::StreamString stream; - stream.PutCString("qPlatform_chmod:"); - stream.PutHex32(file_permissions); - stream.PutChar(','); - stream.PutCStringAsRawHex8(path.c_str()); - const char *packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) - return Error("failed to send '%s' packet", packet); - - if (response.GetChar() != 'F') - return Error("invalid response to '%s' packet", packet); - - return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); -} - -static uint64_t -ParseHostIOPacketResponse (StringExtractorGDBRemote &response, - uint64_t fail_result, - Error &error) -{ - response.SetFilePos(0); - if (response.GetChar() != 'F') - return fail_result; - int32_t result = response.GetS32 (-2); - if (result == -2) - return fail_result; - if (response.GetChar() == ',') - { - int result_errno = response.GetS32 (-2); - if (result_errno != -2) - error.SetError(result_errno, eErrorTypePOSIX); - else - error.SetError(-1, eErrorTypeGeneric); - } + return Error("malformed reply"); + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); + if (exitcode == UINT32_MAX) + return Error("unable to run remote process"); + else if (status_ptr) + *status_ptr = exitcode; + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); + if (signo_ptr) + *signo_ptr = signo; + if (response.GetChar() != ',') + return Error("malformed reply"); + std::string output; + response.GetEscapedBinaryData(output); + if (command_output) + command_output->assign(output); + return Error(); + } + return Error("unable to send packet"); +} + +Error GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, + uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; + lldb_private::StreamString stream; + stream.PutCString("qPlatform_mkdir:"); + stream.PutHex32(file_permissions); + stream.PutChar(','); + stream.PutCStringAsRawHex8(path.c_str()); + llvm::StringRef packet = stream.GetString(); + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response, false) != + PacketResult::Success) + return Error("failed to send '%s' packet", packet.str().c_str()); + + if (response.GetChar() != 'F') + return Error("invalid response to '%s' packet", packet.str().c_str()); + + return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); +} + +Error GDBRemoteCommunicationClient::SetFilePermissions( + const FileSpec &file_spec, uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; + lldb_private::StreamString stream; + stream.PutCString("qPlatform_chmod:"); + stream.PutHex32(file_permissions); + stream.PutChar(','); + stream.PutCStringAsRawHex8(path.c_str()); + llvm::StringRef packet = stream.GetString(); + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response, false) != + PacketResult::Success) + return Error("failed to send '%s' packet", stream.GetData()); + + if (response.GetChar() != 'F') + return Error("invalid response to '%s' packet", stream.GetData()); + + return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); +} + +static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, + uint64_t fail_result, Error &error) { + response.SetFilePos(0); + if (response.GetChar() != 'F') + return fail_result; + int32_t result = response.GetS32(-2); + if (result == -2) + return fail_result; + if (response.GetChar() == ',') { + int result_errno = response.GetS32(-2); + if (result_errno != -2) + error.SetError(result_errno, eErrorTypePOSIX); else - error.Clear(); - return result; + error.SetError(-1, eErrorTypeGeneric); + } else + error.Clear(); + return result; } lldb::user_id_t -GDBRemoteCommunicationClient::OpenFile (const lldb_private::FileSpec& file_spec, - uint32_t flags, - mode_t mode, - Error &error) -{ - std::string path(file_spec.GetPath(false)); - lldb_private::StreamString stream; - stream.PutCString("vFile:open:"); - if (path.empty()) - return UINT64_MAX; - stream.PutCStringAsRawHex8(path.c_str()); - stream.PutChar(','); - stream.PutHex32(flags); - stream.PutChar(','); - stream.PutHex32(mode); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - return ParseHostIOPacketResponse (response, UINT64_MAX, error); - } +GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, + uint32_t flags, mode_t mode, + Error &error) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:open:"); + if (path.empty()) return UINT64_MAX; -} - -bool -GDBRemoteCommunicationClient::CloseFile (lldb::user_id_t fd, - Error &error) -{ - lldb_private::StreamString stream; - stream.Printf("vFile:close:%i", (int)fd); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - return ParseHostIOPacketResponse (response, -1, error) == 0; - } - return false; + stream.PutCStringAsRawHex8(path.c_str()); + stream.PutChar(','); + stream.PutHex32(flags); + stream.PutChar(','); + stream.PutHex32(mode); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + return ParseHostIOPacketResponse(response, UINT64_MAX, error); + } + return UINT64_MAX; +} + +bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, Error &error) { + lldb_private::StreamString stream; + stream.Printf("vFile:close:%i", (int)fd); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + return ParseHostIOPacketResponse(response, -1, error) == 0; + } + return false; } // Extension of host I/O packets to get the file size. -lldb::user_id_t -GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_spec) -{ - std::string path(file_spec.GetPath(false)); - lldb_private::StreamString stream; - stream.PutCString("vFile:size:"); - stream.PutCStringAsRawHex8(path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - return UINT64_MAX; - uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); - return retcode; - } - return UINT64_MAX; +lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( + const lldb_private::FileSpec &file_spec) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:size:"); + stream.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return UINT64_MAX; + uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); + return retcode; + } + return UINT64_MAX; +} + +Error GDBRemoteCommunicationClient::GetFilePermissions( + const FileSpec &file_spec, uint32_t &file_permissions) { + std::string path{file_spec.GetPath(false)}; + Error error; + lldb_private::StreamString stream; + stream.PutCString("vFile:mode:"); + stream.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') { + error.SetErrorStringWithFormat("invalid response to '%s' packet", + stream.GetData()); + } else { + const uint32_t mode = response.GetS32(-1); + if (static_cast<int32_t>(mode) == -1) { + if (response.GetChar() == ',') { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + else + error.SetErrorToGenericError(); + } else + error.SetErrorToGenericError(); + } else { + file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } + } + } else { + error.SetErrorStringWithFormat("failed to send '%s' packet", + stream.GetData()); + } + return error; +} + +uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, + uint64_t offset, void *dst, + uint64_t dst_len, + Error &error) { + lldb_private::StreamString stream; + stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, + offset); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return 0; + uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); + if (retcode == UINT32_MAX) + return retcode; + const char next = (response.Peek() ? *response.Peek() : 0); + if (next == ',') + return 0; + if (next == ';') { + response.GetChar(); // skip the semicolon + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) { + const uint64_t data_to_write = + std::min<uint64_t>(dst_len, buffer.size()); + if (data_to_write > 0) + memcpy(dst, &buffer[0], data_to_write); + return data_to_write; + } + } + } + return 0; +} + +uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, + uint64_t offset, + const void *src, + uint64_t src_len, + Error &error) { + lldb_private::StreamGDBRemote stream; + stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); + stream.PutEscapedBytes(src, src_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') { + error.SetErrorStringWithFormat("write file failed"); + return 0; + } + uint64_t bytes_written = response.GetU64(UINT64_MAX); + if (bytes_written == UINT64_MAX) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return 0; + } + return bytes_written; + } else { + error.SetErrorString("failed to send vFile:pwrite packet"); + } + return 0; +} + +Error GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, + const FileSpec &dst) { + std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; + Error error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:symlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutCStringAsRawHex8(dst_path.c_str()); + stream.PutChar(','); + stream.PutCStringAsRawHex8(src_path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() == 'F') { + uint32_t result = response.GetU32(UINT32_MAX); + if (result != 0) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } else { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("symlink failed"); + } + } else { + error.SetErrorString("failed to send vFile:symlink packet"); + } + return error; +} + +Error GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { + std::string path{file_spec.GetPath(false)}; + Error error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:unlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() == 'F') { + uint32_t result = response.GetU32(UINT32_MAX); + if (result != 0) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } else { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("unlink failed"); + } + } else { + error.SetErrorString("failed to send vFile:unlink packet"); + } + return error; } -Error -GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, - uint32_t &file_permissions) -{ - std::string path{file_spec.GetPath(false)}; - Error error; - lldb_private::StreamString stream; - stream.PutCString("vFile:mode:"); - stream.PutCStringAsRawHex8(path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - { - error.SetErrorStringWithFormat ("invalid response to '%s' packet", packet); - } - else - { - const uint32_t mode = response.GetS32(-1); - if (static_cast<int32_t>(mode) == -1) - { - if (response.GetChar() == ',') - { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); - else - error.SetErrorToGenericError(); - } - else - error.SetErrorToGenericError(); - } - else - { - file_permissions = mode & (S_IRWXU|S_IRWXG|S_IRWXO); - } - } - } - else - { - error.SetErrorStringWithFormat ("failed to send '%s' packet", packet); - } - return error; -} +// Extension of host I/O packets to get whether a file exists. +bool GDBRemoteCommunicationClient::GetFileExists( + const lldb_private::FileSpec &file_spec) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:exists:"); + stream.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + bool retcode = (response.GetChar() != '0'); + return retcode; + } + return false; +} + +bool GDBRemoteCommunicationClient::CalculateMD5( + const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:MD5:"); + stream.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + if (response.Peek() && *response.Peek() == 'x') + return false; + low = response.GetHexMaxU64(false, UINT64_MAX); + high = response.GetHexMaxU64(false, UINT64_MAX); + return true; + } + return false; +} + +bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) { + // Some targets have issues with g/G packets and we need to avoid using them + if (m_avoid_g_packets == eLazyBoolCalculate) { + if (process) { + m_avoid_g_packets = eLazyBoolNo; + const ArchSpec &arch = process->GetTarget().GetArchitecture(); + if (arch.IsValid() && + arch.GetTriple().getVendor() == llvm::Triple::Apple && + arch.GetTriple().getOS() == llvm::Triple::IOS && + arch.GetTriple().getArch() == llvm::Triple::aarch64) { + m_avoid_g_packets = eLazyBoolYes; + uint32_t gdb_server_version = GetGDBServerProgramVersion(); + if (gdb_server_version != 0) { + const char *gdb_server_name = GetGDBServerProgramName(); + if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { + if (gdb_server_version >= 310) + m_avoid_g_packets = eLazyBoolNo; + } + } + } + } + } + return m_avoid_g_packets == eLazyBoolYes; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, + uint32_t reg) { + StreamString payload; + payload.Printf("p%x", reg); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response, false) != PacketResult::Success || + !response.IsNormalResponse()) + return nullptr; + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) { + StreamString payload; + payload.PutChar('g'); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response, false) != PacketResult::Success || + !response.IsNormalResponse()) + return nullptr; + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} + +bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, + uint32_t reg_num, + llvm::ArrayRef<uint8_t> data) { + StreamString payload; + payload.Printf("P%x=", reg_num); + payload.PutBytesAsRawHex8(data.data(), data.size(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + StringExtractorGDBRemote response; + return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), + response, false) == + PacketResult::Success && + response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::WriteAllRegisters( + lldb::tid_t tid, llvm::ArrayRef<uint8_t> data) { + StreamString payload; + payload.PutChar('G'); + payload.PutBytesAsRawHex8(data.data(), data.size(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + StringExtractorGDBRemote response; + return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), + response, false) == + PacketResult::Success && + response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, + uint32_t &save_id) { + save_id = 0; // Set to invalid save ID + if (m_supports_QSaveRegisterState == eLazyBoolNo) + return false; -uint64_t -GDBRemoteCommunicationClient::ReadFile (lldb::user_id_t fd, - uint64_t offset, - void *dst, - uint64_t dst_len, - Error &error) -{ - lldb_private::StreamString stream; - stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - return 0; - uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); - if (retcode == UINT32_MAX) - return retcode; - const char next = (response.Peek() ? *response.Peek() : 0); - if (next == ',') - return 0; - if (next == ';') - { - response.GetChar(); // skip the semicolon - std::string buffer; - if (response.GetEscapedBinaryData(buffer)) - { - const uint64_t data_to_write = std::min<uint64_t>(dst_len, buffer.size()); - if (data_to_write > 0) - memcpy(dst, &buffer[0], data_to_write); - return data_to_write; - } - } - } - return 0; -} + m_supports_QSaveRegisterState = eLazyBoolYes; + StreamString payload; + payload.PutCString("QSaveRegisterState"); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response, false) != PacketResult::Success) + return false; -uint64_t -GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, - uint64_t offset, - const void* src, - uint64_t src_len, - Error &error) -{ - lldb_private::StreamGDBRemote stream; - stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); - stream.PutEscapedBytes(src, src_len); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - { - error.SetErrorStringWithFormat("write file failed"); - return 0; - } - uint64_t bytes_written = response.GetU64(UINT64_MAX); - if (bytes_written == UINT64_MAX) - { - error.SetErrorToGenericError(); - if (response.GetChar() == ',') - { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); - } - return 0; - } - return bytes_written; - } - else - { - error.SetErrorString ("failed to send vFile:pwrite packet"); - } - return 0; -} + if (response.IsUnsupportedResponse()) + m_supports_QSaveRegisterState = eLazyBoolNo; -Error -GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, const FileSpec &dst) -{ - std::string src_path{src.GetPath(false)}, - dst_path{dst.GetPath(false)}; - Error error; - lldb_private::StreamGDBRemote stream; - stream.PutCString("vFile:symlink:"); - // the unix symlink() command reverses its parameters where the dst if first, - // so we follow suit here - stream.PutCStringAsRawHex8(dst_path.c_str()); - stream.PutChar(','); - stream.PutCStringAsRawHex8(src_path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() == 'F') - { - uint32_t result = response.GetU32(UINT32_MAX); - if (result != 0) - { - error.SetErrorToGenericError(); - if (response.GetChar() == ',') - { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); - } - } - } - else - { - // Should have returned with 'F<result>[,<errno>]' - error.SetErrorStringWithFormat("symlink failed"); - } - } - else - { - error.SetErrorString ("failed to send vFile:symlink packet"); - } - return error; -} + const uint32_t response_save_id = response.GetU32(0); + if (response_save_id == 0) + return false; -Error -GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) -{ - std::string path{file_spec.GetPath(false)}; - Error error; - lldb_private::StreamGDBRemote stream; - stream.PutCString("vFile:unlink:"); - // the unix symlink() command reverses its parameters where the dst if first, - // so we follow suit here - stream.PutCStringAsRawHex8(path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() == 'F') - { - uint32_t result = response.GetU32(UINT32_MAX); - if (result != 0) - { - error.SetErrorToGenericError(); - if (response.GetChar() == ',') - { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); - } - } - } - else - { - // Should have returned with 'F<result>[,<errno>]' - error.SetErrorStringWithFormat("unlink failed"); - } - } - else - { - error.SetErrorString ("failed to send vFile:unlink packet"); - } - return error; + save_id = response_save_id; + return true; } -// Extension of host I/O packets to get whether a file exists. -bool -GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) -{ - std::string path(file_spec.GetPath(false)); - lldb_private::StreamString stream; - stream.PutCString("vFile:exists:"); - stream.PutCStringAsRawHex8(path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - return false; - if (response.GetChar() != ',') - return false; - bool retcode = (response.GetChar() != '0'); - return retcode; - } +bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid, + uint32_t save_id) { + // We use the "m_supports_QSaveRegisterState" variable here because the + // QSaveRegisterState and QRestoreRegisterState packets must both be supported + // in + // order to be useful + if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; -} -bool -GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_spec, - uint64_t &high, - uint64_t &low) -{ - std::string path(file_spec.GetPath(false)); - lldb_private::StreamString stream; - stream.PutCString("vFile:MD5:"); - stream.PutCStringAsRawHex8(path.c_str()); - const char* packet = stream.GetData(); - int packet_len = stream.GetSize(); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - if (response.GetChar() != 'F') - return false; - if (response.GetChar() != ',') - return false; - if (response.Peek() && *response.Peek() == 'x') - return false; - low = response.GetHexMaxU64(false, UINT64_MAX); - high = response.GetHexMaxU64(false, UINT64_MAX); - return true; - } + StreamString payload; + payload.Printf("QRestoreRegisterState:%u", save_id); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response, false) != PacketResult::Success) return false; -} -bool -GDBRemoteCommunicationClient::AvoidGPackets (ProcessGDBRemote *process) -{ - // Some targets have issues with g/G packets and we need to avoid using them - if (m_avoid_g_packets == eLazyBoolCalculate) - { - if (process) - { - m_avoid_g_packets = eLazyBoolNo; - const ArchSpec &arch = process->GetTarget().GetArchitecture(); - if (arch.IsValid() - && arch.GetTriple().getVendor() == llvm::Triple::Apple - && arch.GetTriple().getOS() == llvm::Triple::IOS - && arch.GetTriple().getArch() == llvm::Triple::aarch64) - { - m_avoid_g_packets = eLazyBoolYes; - uint32_t gdb_server_version = GetGDBServerProgramVersion(); - if (gdb_server_version != 0) - { - const char *gdb_server_name = GetGDBServerProgramName(); - if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) - { - if (gdb_server_version >= 310) - m_avoid_g_packets = eLazyBoolNo; - } - } - } - } - } - return m_avoid_g_packets == eLazyBoolYes; + if (response.IsOKResponse()) + return true; + + if (response.IsUnsupportedResponse()) + m_supports_QSaveRegisterState = eLazyBoolNo; + return false; } -bool -GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, StringExtractorGDBRemote &response) -{ - Mutex::Locker locker; - if (GetSequenceMutex (locker, "Didn't get sequence mutex for p packet.")) - { - const bool thread_suffix_supported = GetThreadSuffixSupported(); - - if (thread_suffix_supported || SetCurrentThread(tid)) - { - char packet[64]; - int packet_len = 0; - if (thread_suffix_supported) - packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, tid); - else - packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg); - assert (packet_len < ((int)sizeof(packet) - 1)); - return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success; - } - } +bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) { + if (!GetSyncThreadStateSupported()) return false; + StreamString packet; + StringExtractorGDBRemote response; + packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid); + return SendPacketAndWaitForResponse(packet.GetString(), response, false) == + GDBRemoteCommunication::PacketResult::Success && + response.IsOKResponse(); } +bool GDBRemoteCommunicationClient::GetModuleInfo( + const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, + ModuleSpec &module_spec) { + if (!m_supports_qModuleInfo) + return false; -bool -GDBRemoteCommunicationClient::ReadAllRegisters (lldb::tid_t tid, StringExtractorGDBRemote &response) -{ - Mutex::Locker locker; - if (GetSequenceMutex (locker, "Didn't get sequence mutex for g packet.")) - { - const bool thread_suffix_supported = GetThreadSuffixSupported(); - - if (thread_suffix_supported || SetCurrentThread(tid)) - { - char packet[64]; - int packet_len = 0; - // Get all registers in one packet - if (thread_suffix_supported) - packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", tid); - else - packet_len = ::snprintf (packet, sizeof(packet), "g"); - assert (packet_len < ((int)sizeof(packet) - 1)); - return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success; - } - } + std::string module_path = module_file_spec.GetPath(false); + if (module_path.empty()) return false; -} -bool -GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save_id) -{ - save_id = 0; // Set to invalid save ID - if (m_supports_QSaveRegisterState == eLazyBoolNo) - return false; - - m_supports_QSaveRegisterState = eLazyBoolYes; - Mutex::Locker locker; - if (GetSequenceMutex (locker, "Didn't get sequence mutex for QSaveRegisterState.")) - { - const bool thread_suffix_supported = GetThreadSuffixSupported(); - if (thread_suffix_supported || SetCurrentThread(tid)) - { - char packet[256]; - if (thread_suffix_supported) - ::snprintf (packet, sizeof(packet), "QSaveRegisterState;thread:%4.4" PRIx64 ";", tid); - else - ::snprintf(packet, sizeof(packet), "QSaveRegisterState"); - - StringExtractorGDBRemote response; - - if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) - { - if (response.IsUnsupportedResponse()) - { - // This packet isn't supported, don't try calling it again - m_supports_QSaveRegisterState = eLazyBoolNo; - } - - const uint32_t response_save_id = response.GetU32(0); - if (response_save_id != 0) - { - save_id = response_save_id; - return true; - } - } - } - } + + StreamString packet; + packet.PutCString("qModuleInfo:"); + packet.PutCStringAsRawHex8(module_path.c_str()); + packet.PutCString(";"); + const auto &triple = arch_spec.GetTriple().getTriple(); + packet.PutCStringAsRawHex8(triple.c_str()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != + PacketResult::Success) return false; -} -bool -GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t save_id) -{ - // We use the "m_supports_QSaveRegisterState" variable here because the - // QSaveRegisterState and QRestoreRegisterState packets must both be supported in - // order to be useful - if (m_supports_QSaveRegisterState == eLazyBoolNo) - return false; - - Mutex::Locker locker; - if (GetSequenceMutex (locker, "Didn't get sequence mutex for QRestoreRegisterState.")) - { - const bool thread_suffix_supported = GetThreadSuffixSupported(); - if (thread_suffix_supported || SetCurrentThread(tid)) - { - char packet[256]; - if (thread_suffix_supported) - ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u;thread:%4.4" PRIx64 ";", save_id, tid); - else - ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u" PRIx64 ";", save_id); - - StringExtractorGDBRemote response; - - if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - return true; - } - else if (response.IsUnsupportedResponse()) - { - // This packet isn't supported, don't try calling this packet or - // QSaveRegisterState again... - m_supports_QSaveRegisterState = eLazyBoolNo; - } - } - } - } + if (response.IsErrorResponse()) return false; -} -bool -GDBRemoteCommunicationClient::GetModuleInfo (const FileSpec& module_file_spec, - const lldb_private::ArchSpec& arch_spec, - ModuleSpec &module_spec) -{ - if (!m_supports_qModuleInfo) - return false; + if (response.IsUnsupportedResponse()) { + m_supports_qModuleInfo = false; + return false; + } + + llvm::StringRef name; + llvm::StringRef value; + + module_spec.Clear(); + module_spec.GetFileSpec() = module_file_spec; + + while (response.GetNameColonValue(name, value)) { + if (name == "uuid" || name == "md5") { + StringExtractor extractor(value); + std::string uuid; + extractor.GetHexByteString(uuid); + module_spec.GetUUID().SetFromCString(uuid.c_str(), uuid.size() / 2); + } else if (name == "triple") { + StringExtractor extractor(value); + std::string triple; + extractor.GetHexByteString(triple); + module_spec.GetArchitecture().SetTriple(triple.c_str()); + } else if (name == "file_offset") { + uint64_t ival = 0; + if (!value.getAsInteger(16, ival)) + module_spec.SetObjectOffset(ival); + } else if (name == "file_size") { + uint64_t ival = 0; + if (!value.getAsInteger(16, ival)) + module_spec.SetObjectSize(ival); + } else if (name == "file_path") { + StringExtractor extractor(value); + std::string path; + extractor.GetHexByteString(path); + module_spec.GetFileSpec() = FileSpec(path, false, arch_spec); + } + } + + return true; +} + +static llvm::Optional<ModuleSpec> +ParseModuleSpec(StructuredData::Dictionary *dict) { + ModuleSpec result; + if (!dict) + return llvm::None; + + std::string string; + uint64_t integer; + + if (!dict->GetValueForKeyAsString("uuid", string)) + return llvm::None; + result.GetUUID().SetFromCString(string.c_str(), string.size()); + + if (!dict->GetValueForKeyAsInteger("file_offset", integer)) + return llvm::None; + result.SetObjectOffset(integer); + + if (!dict->GetValueForKeyAsInteger("file_size", integer)) + return llvm::None; + result.SetObjectSize(integer); + + if (!dict->GetValueForKeyAsString("triple", string)) + return llvm::None; + result.GetArchitecture().SetTriple(string.c_str()); + + if (!dict->GetValueForKeyAsString("file_path", string)) + return llvm::None; + result.GetFileSpec() = FileSpec(string, false, result.GetArchitecture()); + + return result; +} + +llvm::Optional<std::vector<ModuleSpec>> +GDBRemoteCommunicationClient::GetModulesInfo( + llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) { + if (!m_supports_jModulesInfo) + return llvm::None; + + JSONArray::SP module_array_sp = std::make_shared<JSONArray>(); + for (const FileSpec &module_file_spec : module_file_specs) { + JSONObject::SP module_sp = std::make_shared<JSONObject>(); + module_array_sp->AppendObject(module_sp); + module_sp->SetObject( + "file", std::make_shared<JSONString>(module_file_spec.GetPath())); + module_sp->SetObject("triple", + std::make_shared<JSONString>(triple.getTriple())); + } + StreamString unescaped_payload; + unescaped_payload.PutCString("jModulesInfo:"); + module_array_sp->Write(unescaped_payload); + StreamGDBRemote payload; + payload.PutEscapedBytes(unescaped_payload.GetString().data(), + unescaped_payload.GetSize()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(payload.GetString(), response, false) != + PacketResult::Success || + response.IsErrorResponse()) + return llvm::None; + + if (response.IsUnsupportedResponse()) { + m_supports_jModulesInfo = false; + return llvm::None; + } + + StructuredData::ObjectSP response_object_sp = + StructuredData::ParseJSON(response.GetStringRef()); + if (!response_object_sp) + return llvm::None; + + StructuredData::Array *response_array = response_object_sp->GetAsArray(); + if (!response_array) + return llvm::None; + + std::vector<ModuleSpec> result; + for (size_t i = 0; i < response_array->GetSize(); ++i) { + if (llvm::Optional<ModuleSpec> module_spec = ParseModuleSpec( + response_array->GetItemAtIndex(i)->GetAsDictionary())) + result.push_back(*module_spec); + } + + return result; +} - std::string module_path = module_file_spec.GetPath (false); - if (module_path.empty ()) - return false; +// query the target remote for extended information using the qXfer packet +// +// example: object='features', annex='target.xml', out=<xml output> +// return: 'true' on success +// 'false' on failure (err set) +bool GDBRemoteCommunicationClient::ReadExtFeature( + const lldb_private::ConstString object, + const lldb_private::ConstString annex, std::string &out, + lldb_private::Error &err) { - StreamString packet; - packet.PutCString("qModuleInfo:"); - packet.PutCStringAsRawHex8(module_path.c_str()); - packet.PutCString(";"); - const auto& triple = arch_spec.GetTriple().getTriple(); - packet.PutCStringAsRawHex8(triple.c_str()); + std::stringstream output; + StringExtractorGDBRemote chunk; - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) != PacketResult::Success) - return false; + uint64_t size = GetRemoteMaxPacketSize(); + if (size == 0) + size = 0x1000; + size = size - 1; // Leave space for the 'm' or 'l' character in the response + int offset = 0; + bool active = true; - if (response.IsErrorResponse ()) - return false; + // loop until all data has been read + while (active) { - if (response.IsUnsupportedResponse ()) - { - m_supports_qModuleInfo = false; - return false; - } + // send query extended feature packet + std::stringstream packet; + packet << "qXfer:" << object.AsCString("") + << ":read:" << annex.AsCString("") << ":" << std::hex << offset + << "," << std::hex << size; - std::string name; - std::string value; - bool success; - StringExtractor extractor; + GDBRemoteCommunication::PacketResult res = + SendPacketAndWaitForResponse(packet.str(), chunk, false); - module_spec.Clear (); - module_spec.GetFileSpec () = module_file_spec; - - while (response.GetNameColonValue (name, value)) - { - if (name == "uuid" || name == "md5") - { - extractor.GetStringRef ().swap (value); - extractor.SetFilePos (0); - extractor.GetHexByteString (value); - module_spec.GetUUID().SetFromCString (value.c_str(), value.size() / 2); - } - else if (name == "triple") - { - extractor.GetStringRef ().swap (value); - extractor.SetFilePos (0); - extractor.GetHexByteString (value); - module_spec.GetArchitecture().SetTriple (value.c_str ()); - } - else if (name == "file_offset") - { - const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); - if (success) - module_spec.SetObjectOffset (ival); - } - else if (name == "file_size") - { - const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); - if (success) - module_spec.SetObjectSize (ival); - } - else if (name == "file_path") - { - extractor.GetStringRef ().swap (value); - extractor.SetFilePos (0); - extractor.GetHexByteString (value); - module_spec.GetFileSpec() = FileSpec(value.c_str(), false, arch_spec); - } + if (res != GDBRemoteCommunication::PacketResult::Success) { + err.SetErrorString("Error sending $qXfer packet"); + return false; } - return true; -} + const std::string &str = chunk.GetStringRef(); + if (str.length() == 0) { + // should have some data in chunk + err.SetErrorString("Empty response from $qXfer packet"); + return false; + } -// query the target remote for extended information using the qXfer packet -// -// example: object='features', annex='target.xml', out=<xml output> -// return: 'true' on success -// 'false' on failure (err set) -bool -GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString object, - const lldb_private::ConstString annex, - std::string & out, - lldb_private::Error & err) { - - std::stringstream output; - StringExtractorGDBRemote chunk; - - uint64_t size = GetRemoteMaxPacketSize(); - if (size == 0) - size = 0x1000; - size = size - 1; // Leave space for the 'm' or 'l' character in the response - int offset = 0; - bool active = true; - - // loop until all data has been read - while ( active ) { - - // send query extended feature packet - std::stringstream packet; - packet << "qXfer:" - << object.AsCString("") << ":read:" - << annex.AsCString("") << ":" - << std::hex << offset << "," - << std::hex << size; - - GDBRemoteCommunication::PacketResult res = - SendPacketAndWaitForResponse( packet.str().c_str(), - chunk, - false ); - - if ( res != GDBRemoteCommunication::PacketResult::Success ) { - err.SetErrorString( "Error sending $qXfer packet" ); - return false; - } + // check packet code + switch (str[0]) { + // last chunk + case ('l'): + active = false; + LLVM_FALLTHROUGH; - const std::string & str = chunk.GetStringRef( ); - if ( str.length() == 0 ) { - // should have some data in chunk - err.SetErrorString( "Empty response from $qXfer packet" ); - return false; - } + // more chunks + case ('m'): + if (str.length() > 1) + output << &str[1]; + offset += size; + break; - // check packet code - switch ( str[0] ) { - // last chunk - case ( 'l' ): - active = false; - LLVM_FALLTHROUGH; - - // more chunks - case ( 'm' ) : - if ( str.length() > 1 ) - output << &str[1]; - offset += size; - break; - - // unknown chunk - default: - err.SetErrorString( "Invalid continuation code from $qXfer packet" ); - return false; - } + // unknown chunk + default: + err.SetErrorString("Invalid continuation code from $qXfer packet"); + return false; } + } - out = output.str( ); - err.Success( ); - return true; + out = output.str(); + 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 +// 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>". // // Three examples: // // lldb sends: qSymbol:: // lldb receives: OK -// Remote gdb stub does not need to know the addresses of any symbols, lldb does not +// Remote gdb stub does not need to know the addresses of any symbols, lldb +// does not // need to ask again in this session. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473 // lldb receives: OK -// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does not know -// the address at this time. lldb needs to send qSymbol:: again when it has more +// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does +// not know +// the address at this time. lldb needs to send qSymbol:: again when it has +// more // solibs loaded. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473 // lldb receives: OK -// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says that it -// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it does not +// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says +// that it +// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it +// does not // need any more symbols. lldb does not need to ask again in this session. -void -GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process) -{ - // Set to true once we've resolved a symbol to an address for the remote stub. - // If we get an 'OK' response after this, the remote stub doesn't need any more - // symbols and we can stop asking. - bool symbol_response_provided = false; - - // Is this the inital qSymbol:: packet? - bool first_qsymbol_query = true; - - if (m_supports_qSymbol && m_qSymbol_requests_done == false) - { - Mutex::Locker locker; - if (GetSequenceMutex(locker, "GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex")) - { - StreamString packet; - packet.PutCString ("qSymbol::"); - StringExtractorGDBRemote response; - while (SendPacketAndWaitForResponseNoLock(packet.GetData(), packet.GetSize(), response) == PacketResult::Success) - { - if (response.IsOKResponse()) - { - if (symbol_response_provided || first_qsymbol_query) - { - m_qSymbol_requests_done = true; - } - - // We are done serving symbols requests - return; - } - first_qsymbol_query = false; +void GDBRemoteCommunicationClient::ServeSymbolLookups( + lldb_private::Process *process) { + // Set to true once we've resolved a symbol to an address for the remote stub. + // If we get an 'OK' response after this, the remote stub doesn't need any + // more + // symbols and we can stop asking. + bool symbol_response_provided = false; + + // Is this the initial qSymbol:: packet? + bool first_qsymbol_query = true; + + if (m_supports_qSymbol && m_qSymbol_requests_done == false) { + Lock lock(*this, false); + if (lock) { + StreamString packet; + packet.PutCString("qSymbol::"); + StringExtractorGDBRemote response; + while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + if (symbol_response_provided || first_qsymbol_query) { + m_qSymbol_requests_done = true; + } + + // We are done serving symbols requests + return; + } + first_qsymbol_query = false; + + 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)) { + const size_t num_scs = sc_list.GetSize(); + for (size_t sc_idx = 0; + sc_idx < num_scs && + symbol_load_addr == LLDB_INVALID_ADDRESS; + ++sc_idx) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(sc_idx, sc)) { + if (sc.symbol) { + switch (sc.symbol->GetType()) { + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeUndefined: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeVariable: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeTrampoline: + break; - 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)) - { - const size_t num_scs = sc_list.GetSize(); - for (size_t sc_idx=0; sc_idx<num_scs && symbol_load_addr == LLDB_INVALID_ADDRESS; ++sc_idx) - { - SymbolContext sc; - if (sc_list.GetContextAtIndex(sc_idx, sc)) - { - if (sc.symbol) - { - switch (sc.symbol->GetType()) - { - case eSymbolTypeInvalid: - case eSymbolTypeAbsolute: - case eSymbolTypeUndefined: - case eSymbolTypeSourceFile: - case eSymbolTypeHeaderFile: - case eSymbolTypeObjectFile: - case eSymbolTypeCommonBlock: - case eSymbolTypeBlock: - case eSymbolTypeLocal: - case eSymbolTypeParam: - case eSymbolTypeVariable: - case eSymbolTypeVariableType: - case eSymbolTypeLineEntry: - case eSymbolTypeLineHeader: - case eSymbolTypeScopeBegin: - case eSymbolTypeScopeEnd: - case eSymbolTypeAdditional: - case eSymbolTypeCompiler: - case eSymbolTypeInstrumentation: - case eSymbolTypeTrampoline: - break; - - case eSymbolTypeCode: - case eSymbolTypeResolver: - case eSymbolTypeData: - case eSymbolTypeRuntime: - case eSymbolTypeException: - case eSymbolTypeObjCClass: - case eSymbolTypeObjCMetaClass: - case eSymbolTypeObjCIVar: - case eSymbolTypeReExported: - symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); - break; - } - } - } - } - } - // 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); - symbol_response_provided = true; - } - else - { - symbol_response_provided = false; - } - 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 - } + case eSymbolTypeCode: + case eSymbolTypeResolver: + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeException: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + case eSymbolTypeReExported: + symbol_load_addr = + sc.symbol->GetLoadAddress(&process->GetTarget()); + break; + } } + } } - } - // If we make it here, the symbol request packet response wasn't valid or - // our symbol lookup failed so we must abort - return; + } + // 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); + symbol_response_provided = true; + } else { + symbol_response_provided = false; + } + 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; + + } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( + GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) { + log->Printf( + "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", + __FUNCTION__); + } + } +} + +StructuredData::Array * +GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { + if (!m_supported_async_json_packets_is_valid) { + // Query the server for the array of supported asynchronous JSON + // packets. + m_supported_async_json_packets_is_valid = true; - } - } -} + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + // Poll it now. + StringExtractorGDBRemote response; + const bool send_async = false; + if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response, + send_async) == PacketResult::Success) { + m_supported_async_json_packets_sp = + StructuredData::ParseJSON(response.GetStringRef()); + if (m_supported_async_json_packets_sp && + !m_supported_async_json_packets_sp->GetAsArray()) { + // We were returned something other than a JSON array. This + // is invalid. Clear it out. + if (log) + log->Printf("GDBRemoteCommunicationClient::%s(): " + "QSupportedAsyncJSONPackets returned invalid " + "result: %s", + __FUNCTION__, response.GetStringRef().c_str()); + m_supported_async_json_packets_sp.reset(); + } + } else { + if (log) + log->Printf("GDBRemoteCommunicationClient::%s(): " + "QSupportedAsyncJSONPackets unsupported", + __FUNCTION__); + } + + if (log && m_supported_async_json_packets_sp) { + StreamString stream; + m_supported_async_json_packets_sp->Dump(stream); + log->Printf("GDBRemoteCommunicationClient::%s(): supported async " + "JSON packets: %s", + __FUNCTION__, stream.GetData()); + } + } + + return m_supported_async_json_packets_sp + ? m_supported_async_json_packets_sp->GetAsArray() + : nullptr; +} + +Error GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( + const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { + Error error; + + if (type_name.GetLength() == 0) { + error.SetErrorString("invalid type_name argument"); + return error; + } + + // Build command: Configure{type_name}: serialized config + // data. + StreamGDBRemote stream; + stream.PutCString("QConfigure"); + stream.PutCString(type_name.AsCString()); + stream.PutChar(':'); + if (config_sp) { + // Gather the plain-text version of the configuration data. + StreamString unescaped_stream; + config_sp->Dump(unescaped_stream); + unescaped_stream.Flush(); + + // Add it to the stream in escaped fashion. + stream.PutEscapedBytes(unescaped_stream.GetString().data(), + unescaped_stream.GetSize()); + } + stream.Flush(); + + // Send the packet. + const bool send_async = false; + StringExtractorGDBRemote response; + auto result = + SendPacketAndWaitForResponse(stream.GetString(), response, send_async); + if (result == PacketResult::Success) { + // We failed if the config result comes back other than OK. + if (strcmp(response.GetStringRef().c_str(), "OK") == 0) { + // Okay! + error.Clear(); + } else { + error.SetErrorStringWithFormat("configuring StructuredData feature " + "%s failed with error %s", + type_name.AsCString(), + response.GetStringRef().c_str()); + } + } else { + // Can we get more data here on the failure? + error.SetErrorStringWithFormat("configuring StructuredData feature %s " + "failed when sending packet: " + "PacketResult=%d", + type_name.AsCString(), (int)result); + } + return error; +} + +void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { + GDBRemoteClientBase::OnRunPacketSent(first); + m_curr_tid = LLDB_INVALID_THREAD_ID; +} |