diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote')
15 files changed, 1051 insertions, 817 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp index 4d4a4f8c5c7a8..4e20b56fb111b 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp @@ -109,16 +109,14 @@ StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse( const bool should_stop = ShouldStop(signals, response); response.SetFilePos(0); - // The packet we should resume with. 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. - // This packet may get modified by the async actions (e.g. to send a - // signal). + // The packet we should resume with. 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. This packet may get + // modified by the async actions (e.g. to send a signal). m_continue_packet = 'c'; cont_lock.unlock(); @@ -177,6 +175,30 @@ GDBRemoteClientBase::SendPacketAndWaitForResponse( } GDBRemoteCommunication::PacketResult +GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport( + llvm::StringRef payload, StringExtractorGDBRemote &response, + bool send_async, + llvm::function_ref<void(llvm::StringRef)> output_callback) { + Lock lock(*this, send_async); + if (!lock) { + if (Log *log = + ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)) + log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending " + "packet '%.*s' (send_async=%d)", + __FUNCTION__, int(payload.size()), payload.data(), + send_async); + return PacketResult::ErrorSendFailed; + } + + PacketResult packet_result = SendPacketNoLock(payload); + if (packet_result != PacketResult::Success) + return packet_result; + + return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true, + output_callback); +} + +GDBRemoteCommunication::PacketResult GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock( llvm::StringRef payload, StringExtractorGDBRemote &response) { PacketResult packet_result = SendPacketNoLock(payload); @@ -239,19 +261,16 @@ bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals, if (m_async_count == 0) return true; // We were not interrupted. The process stopped on its own. - // Older debugserver stubs (before April 2016) can return two - // stop-reply packets in response to a ^C packet. - // Additionally, all debugservers still return two stop replies if - // the inferior stops due to some other reason before the remote - // stub manages to interrupt it. We need to wait for this - // additional packet to make sure the packet sequence does not get - // skewed. + // Older debugserver stubs (before April 2016) can return two stop-reply + // packets in response to a ^C packet. Additionally, all debugservers still + // return two stop replies if the inferior stops due to some other reason + // before the remote stub manages to interrupt it. We need to wait for this + // additional packet to make sure the packet sequence does not get skewed. StringExtractorGDBRemote extra_stop_reply_packet; ReadPacket(extra_stop_reply_packet, milliseconds(100), false); - // Interrupting is typically done using SIGSTOP or SIGINT, so if - // the process stops with some other signal, we definitely want to - // stop. + // Interrupting is typically done using SIGSTOP or SIGINT, so if the process + // stops with some other signal, we definitely want to stop. const uint8_t signo = response.GetHexU8(UINT8_MAX); if (signo != signals.GetSignalNumberFromName("SIGSTOP") && signo != signals.GetSignalNumberFromName("SIGINT")) diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h index 2646405c9b916..3d84ce0ebe188 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h @@ -48,6 +48,11 @@ public: StringExtractorGDBRemote &response, bool send_async); + PacketResult SendPacketAndReceiveResponseWithOutputSupport( + llvm::StringRef payload, StringExtractorGDBRemote &response, + bool send_async, + llvm::function_ref<void(llvm::StringRef)> output_callback); + bool SendvContPacket(llvm::StringRef payload, StringExtractorGDBRemote &response); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index 949cf19db658a..c335b60028619 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -150,9 +150,8 @@ GDBRemoteCommunication::~GDBRemoteCommunication() { Disconnect(); } - // Stop the communications read thread which is used to parse all - // incoming packets. This function will block until the read - // thread returns. + // Stop the communications read thread which is used to parse all incoming + // packets. This function will block until the read thread returns. if (m_read_thread_enabled) StopReadThread(); } @@ -217,12 +216,10 @@ GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) { } } - // If logging was just enabled and we have history, then dump out what - // we have to the log so we get the historical context. The Dump() call - // that + // If logging was just enabled and we have history, then dump out what we + // have to the log so we get the historical context. The Dump() call that // logs all of the packet will set a boolean so that we don't dump this - // more - // than once + // more than once if (!m_history.DidDumpToLog()) m_history.Dump(log); @@ -275,6 +272,23 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck() { } GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::ReadPacketWithOutputSupport( + StringExtractorGDBRemote &response, Timeout<std::micro> timeout, + bool sync_on_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback) { + auto result = ReadPacket(response, timeout, sync_on_timeout); + while (result == PacketResult::Success && response.IsNormalResponse() && + response.PeekChar() == 'O') { + response.GetChar(); + std::string output; + if (response.GetHexByteString(output)) + output_callback(output); + result = ReadPacket(response, timeout, sync_on_timeout); + } + return result; +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response, Timeout<std::micro> timeout, bool sync_on_timeout) { @@ -286,8 +300,8 @@ GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response, // This function is called when a packet is requested. // A whole packet is popped from the packet queue and returned to the caller. -// Packets are placed into this queue from the communication read thread. -// See GDBRemoteCommunication::AppendBytesToCache. +// Packets are placed into this queue from the communication read thread. See +// GDBRemoteCommunication::AppendBytesToCache. GDBRemoteCommunication::PacketResult GDBRemoteCommunication::PopPacketFromQueue(StringExtractorGDBRemote &response, Timeout<std::micro> timeout) { @@ -407,11 +421,9 @@ GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet, break; } else if (successful_responses == 1) { // We got something else back as the first successful - // response, it probably is - // the response to the packet we actually wanted, so copy it - // over if this - // is the first success and continue to try to get the qEcho - // response + // response, it probably is the response to the packet we + // actually wanted, so copy it over if this is the first + // success and continue to try to get the qEcho response packet = echo_response; got_actual_response = true; } @@ -424,14 +436,13 @@ GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet, } // We weren't able to sync back up with the server, we must abort - // otherwise - // all responses might not be from the right packets... + // otherwise all responses might not be from the right packets... if (sync_success) { // We timed out, but were able to recover if (got_actual_response) { // We initially timed out, but we did get a response that came in - // before the successful - // reply to our qEcho packet, so lets say everything is fine... + // before the successful reply to our qEcho packet, so lets say + // everything is fine... return PacketResult::Success; } } else { @@ -473,10 +484,9 @@ bool GDBRemoteCommunication::DecompressPacket() { size_t pkt_size = m_bytes.size(); - // Smallest possible compressed packet is $N#00 - an uncompressed empty reply, - // most commonly indicating - // an unsupported packet. Anything less than 5 characters, it's definitely - // not a compressed packet. + // Smallest possible compressed packet is $N#00 - an uncompressed empty + // reply, most commonly indicating an unsupported packet. Anything less than + // 5 characters, it's definitely not a compressed packet. if (pkt_size < 5) return true; @@ -505,17 +515,15 @@ bool GDBRemoteCommunication::DecompressPacket() { 1; // The first character of the two hex checksum characters // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain - // multiple packets. - // size_of_first_packet is the size of the initial packet which we'll replace - // with the decompressed - // version of, leaving the rest of m_bytes unmodified. + // multiple packets. size_of_first_packet is the size of the initial packet + // which we'll replace with the decompressed version of, leaving the rest of + // m_bytes unmodified. size_t size_of_first_packet = hash_mark_idx + 3; // Compressed packets ("$C") start with a base10 number which is the size of - // the uncompressed payload, - // then a : and then the compressed data. e.g. $C1024:<binary>#00 - // Update content_start and content_length to only include the <binary> part - // of the packet. + // the uncompressed payload, then a : and then the compressed data. e.g. + // $C1024:<binary>#00 Update content_start and content_length to only include + // the <binary> part of the packet. uint64_t decompressed_bufsize = ULONG_MAX; if (m_bytes[1] == 'C') { @@ -564,15 +572,14 @@ bool GDBRemoteCommunication::DecompressPacket() { } if (m_bytes[1] == 'N') { - // This packet was not compressed -- delete the 'N' character at the - // start and the packet may be processed as-is. + // This packet was not compressed -- delete the 'N' character at the start + // and the packet may be processed as-is. m_bytes.erase(1, 1); return true; } - // Reverse the gdb-remote binary escaping that was done to the compressed text - // to - // guard characters like '$', '#', '}', etc. + // Reverse the gdb-remote binary escaping that was done to the compressed + // text to guard characters like '$', '#', '}', etc. std::vector<uint8_t> unescaped_content; unescaped_content.reserve(content_length); size_t i = content_start; @@ -613,12 +620,10 @@ bool GDBRemoteCommunication::DecompressPacket() { else if (m_compression_type == CompressionType::LZMA) compression_type = COMPRESSION_LZMA; - // If we have the expected size of the decompressed payload, we can allocate - // the right-sized buffer and do it. If we don't have that information, - // we'll - // need to try decoding into a big buffer and if the buffer wasn't big - // enough, - // increase it and try again. + // If we have the expected size of the decompressed payload, we can + // allocate the right-sized buffer and do it. If we don't have that + // information, we'll need to try decoding into a big buffer and if the + // buffer wasn't big enough, increase it and try again. if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) { decompressed_bytes = compression_decode_buffer( @@ -706,9 +711,8 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, // Parse up the packets into gdb remote packets if (!m_bytes.empty()) { - // end_idx must be one past the last valid packet byte. Start - // it off with an invalid value that is the same as the current - // index. + // end_idx must be one past the last valid packet byte. Start it off with + // an invalid value that is the same as the current index. size_t content_start = 0; size_t content_length = 0; size_t total_length = 0; @@ -743,7 +747,8 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, checksum_idx = hash_pos + 1; // Skip the dollar sign content_start = 1; - // Don't include the # in the content or the $ in the content length + // Don't include the # in the content or the $ in the content + // length content_length = hash_pos - 1; total_length = @@ -757,11 +762,10 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, break; default: { - // We have an unexpected byte and we need to flush all bad - // data that is in m_bytes, so we need to find the first - // byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt), - // or '$' character (start of packet header) or of course, - // the end of the data in m_bytes... + // We have an unexpected byte and we need to flush all bad data that is + // in m_bytes, so we need to find the first byte that is a '+' (ACK), '-' + // (NACK), \x03 (CTRL+C interrupt), or '$' character (start of packet + // header) or of course, the end of the data in m_bytes... const size_t bytes_len = m_bytes.size(); bool done = false; uint32_t idx; @@ -802,16 +806,14 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, if (log) { // If logging was just enabled and we have history, then dump out what // we have to the log so we get the historical context. The Dump() call - // that - // logs all of the packet will set a boolean so that we don't dump this - // more - // than once + // that logs all of the packet will set a boolean so that we don't dump + // this more than once if (!m_history.DidDumpToLog()) m_history.Dump(log); bool binary = false; - // Only detect binary for packets that start with a '$' and have a '#CC' - // checksum + // Only detect binary for packets that start with a '$' and have a + // '#CC' checksum if (m_bytes[0] == '$' && total_length > 4) { for (size_t i = 0; !binary && i < total_length; ++i) { unsigned char c = m_bytes[i]; @@ -834,8 +836,8 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, // Remove binary escaped bytes when displaying the packet... const char ch = m_bytes[i]; if (ch == 0x7d) { - // 0x7d is the escape character. The next character is to - // be XOR'd with 0x20. + // 0x7d is the escape character. The next character is to be + // XOR'd with 0x20. const char escapee = m_bytes[++i] ^ 0x20; strm.Printf("%2.2x", escapee); } else { @@ -863,26 +865,25 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, // Clear packet_str in case there is some existing data in it. packet_str.clear(); - // Copy the packet from m_bytes to packet_str expanding the - // run-length encoding in the process. - // Reserve enough byte for the most common case (no RLE used) + // Copy the packet from m_bytes to packet_str expanding the run-length + // encoding in the process. Reserve enough byte for the most common case + // (no RLE used) packet_str.reserve(m_bytes.length()); for (std::string::const_iterator c = m_bytes.begin() + content_start; c != m_bytes.begin() + content_end; ++c) { if (*c == '*') { - // '*' indicates RLE. Next character will give us the - // repeat count and previous character is what is to be - // repeated. + // '*' indicates RLE. Next character will give us the repeat count + // and previous character is what is to be repeated. char char_to_repeat = packet_str.back(); // Number of time the previous character is repeated int repeat_count = *++c + 3 - ' '; - // We have the char_to_repeat and repeat_count. Now push - // it in the packet. + // We have the char_to_repeat and repeat_count. Now push it in the + // packet. for (int i = 0; i < repeat_count; ++i) packet_str.push_back(char_to_repeat); } else if (*c == 0x7d) { - // 0x7d is the escape character. The next character is to - // be XOR'd with 0x20. + // 0x7d is the escape character. The next character is to be XOR'd + // with 0x20. char escapee = *++c ^ 0x20; packet_str.push_back(escapee); } else { @@ -897,7 +898,8 @@ GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, if (GetSendAcks()) { const char *packet_checksum_cstr = &m_bytes[checksum_idx]; char packet_checksum = strtol(packet_checksum_cstr, NULL, 16); - char actual_checksum = CalculcateChecksum(packet_str); + char actual_checksum = CalculcateChecksum( + llvm::StringRef(m_bytes).slice(content_start, content_end)); success = packet_checksum == actual_checksum; if (!success) { if (log) @@ -991,11 +993,12 @@ Status GDBRemoteCommunication::StartDebugserverProcess( char debugserver_path[PATH_MAX]; FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); - // Always check to see if we have an environment override for the path - // to the debugserver to use and use it if we do. + // Always check to see if we have an environment override for the path to the + // debugserver to use and use it if we do. const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); if (env_debugserver_path) { - debugserver_file_spec.SetFile(env_debugserver_path, false); + debugserver_file_spec.SetFile(env_debugserver_path, false, + FileSpec::Style::native); if (log) log->Printf("GDBRemoteCommunication::%s() gdb-remote stub exe path set " "from environment variable: %s", @@ -1004,10 +1007,9 @@ Status GDBRemoteCommunication::StartDebugserverProcess( debugserver_file_spec = g_debugserver_file_spec; bool debugserver_exists = debugserver_file_spec.Exists(); if (!debugserver_exists) { - // The debugserver binary is in the LLDB.framework/Resources - // directory. - if (HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, - debugserver_file_spec)) { + // The debugserver binary is in the LLDB.framework/Resources directory. + debugserver_file_spec = HostInfo::GetSupportExeDir(); + if (debugserver_file_spec) { debugserver_file_spec.AppendPathComponent(DEBUGSERVER_BASENAME); debugserver_exists = debugserver_file_spec.Exists(); if (debugserver_exists) { @@ -1031,8 +1033,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( __FUNCTION__, debugserver_file_spec.GetPath().c_str()); } // Don't cache the platform specific GDB server binary as it could - // change - // from platform to platform + // change from platform to platform g_debugserver_file_spec.Clear(); } } @@ -1080,14 +1081,14 @@ Status GDBRemoteCommunication::StartDebugserverProcess( // once data is written to the pipe, debug server is up and running. Pipe socket_pipe; - // port is null when debug server should listen on domain socket - - // we're not interested in port value but rather waiting for debug server - // to become available. + // port is null when debug server should listen on domain socket - we're + // not interested in port value but rather waiting for debug server to + // become available. if (pass_comm_fd == -1) { if (url) { -// Create a temporary file to get the stdout/stderr and redirect the -// output of the command into this file. We will later read this file -// if all goes well and fill the data into "command_output_ptr" +// Create a temporary file to get the stdout/stderr and redirect the output of +// the command into this file. We will later read this file if all goes well +// and fill the data into "command_output_ptr" #if defined(__APPLE__) // Binding to port zero, we need to figure out what port it ends up // using using a named pipe... @@ -1120,8 +1121,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( #endif } else { // No host and port given, so lets listen on our end and make the - // debugserver - // connect to us.. + // debugserver connect to us.. error = StartListenThread("127.0.0.1", 0); if (error.Fail()) { if (log) @@ -1134,7 +1134,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection(); // Wait for 10 seconds to resolve the bound port - uint16_t port_ = connection->GetListeningPort(10); + uint16_t port_ = connection->GetListeningPort(std::chrono::seconds(10)); if (port_ > 0) { char port_cstr[32]; snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_); @@ -1206,11 +1206,7 @@ Status GDBRemoteCommunication::StartDebugserverProcess( } // Copy the current environment to the gdbserver/debugserver instance - StringList env; - if (Host::GetEnvironment(env)) { - for (size_t i = 0; i < env.GetSize(); ++i) - launch_info.GetEnvironmentEntries().AppendArgument(env[i]); - } + launch_info.GetEnvironment() = Host::GetEnvironment(); // Close STDIN, STDOUT and STDERR. launch_info.AppendCloseFileAction(STDIN_FILENO); @@ -1327,14 +1323,11 @@ GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout() { } // This function is called via the Communications class read thread when bytes -// become available -// for this connection. This function will consume all incoming bytes and try to -// parse whole -// packets as they become available. Full packets are placed in a queue, so that -// all packet -// requests can simply pop from this queue. Async notification packets will be -// dispatched -// immediately to the ProcessGDBRemote Async thread via an event. +// become available for this connection. This function will consume all +// incoming bytes and try to parse whole packets as they become available. Full +// packets are placed in a queue, so that all packet requests can simply pop +// from this queue. Async notification packets will be dispatched immediately +// to the ProcessGDBRemote Async thread via an event. void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes, size_t len, bool broadcast, lldb::ConnectionStatus status) { @@ -1343,8 +1336,8 @@ void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes, while (true) { PacketType type = CheckForPacket(bytes, len, packet); - // scrub the data so we do not pass it back to CheckForPacket - // on future passes of the loop + // scrub the data so we do not pass it back to CheckForPacket on future + // passes of the loop bytes = nullptr; len = 0; @@ -1368,8 +1361,8 @@ void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes, // put this packet into an event const char *pdata = packet.GetStringRef().c_str(); - // as the communication class, we are a broadcaster and the - // async thread is tuned to listen to us + // as the communication class, we are a broadcaster and the async thread + // is tuned to listen to us BroadcastEvent(eBroadcastBitGdbReadThreadGotNotify, new EventDataBytes(pdata)); } diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index ecc9386e49c7d..67796e4c61ef2 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -24,10 +24,10 @@ #include "lldb/Core/Listener.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/Predicate.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Args.h" #include "lldb/lldb-public.h" -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" namespace lldb_private { namespace process_gdb_remote { @@ -232,6 +232,11 @@ protected: PacketResult ReadPacket(StringExtractorGDBRemote &response, Timeout<std::micro> timeout, bool sync_on_timeout); + PacketResult ReadPacketWithOutputSupport( + StringExtractorGDBRemote &response, Timeout<std::micro> timeout, + bool sync_on_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback); + // Pop a packet from the queue in a thread safe manner PacketResult PopPacketFromQueue(StringExtractorGDBRemote &response, Timeout<std::micro> timeout); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 867f57c475cec..c8b59d5d236b9 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -21,11 +21,12 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Host/XML.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" @@ -35,8 +36,8 @@ // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" -#include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Config.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" #include "llvm/ADT/StringSwitch.h" @@ -81,6 +82,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() m_supports_qXfer_libraries_read(eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), m_supports_qXfer_features_read(eLazyBoolCalculate), + m_supports_qXfer_memory_map_read(eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), @@ -98,12 +100,12 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() 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_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() {} + 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(), m_qXfer_memory_map(), + m_qXfer_memory_map_loaded(false) {} //---------------------------------------------------------------------- // Destructor @@ -116,8 +118,8 @@ GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *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... + // 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 @@ -127,9 +129,9 @@ bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { packet_result = ReadPacket(response, milliseconds(10), 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... + // 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 { @@ -192,6 +194,13 @@ bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { return m_supports_qXfer_features_read == eLazyBoolYes; } +bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() { + if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_memory_map_read == eLazyBoolYes; +} + uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { if (m_max_packet_size == 0) { GetRemoteQSupported(); @@ -205,9 +214,8 @@ bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { 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. + // 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))); @@ -296,6 +304,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate; + m_supports_qXfer_memory_map_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; m_supports_qfProcessInfo = true; @@ -313,9 +322,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { 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_version = llvm::VersionTuple(); m_os_build.clear(); m_os_kernel.clear(); m_hostname.clear(); @@ -329,8 +336,8 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { m_supports_jModulesInfo = true; } - // These flags should be reset when we first connect to a GDB server - // and when our inferior process execs + // 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(); } @@ -342,6 +349,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo; + m_supports_qXfer_memory_map_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if // not, we assume no limit @@ -361,8 +369,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { 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. + // configuration of the transport before attaching/launching the process. m_qSupported_response = response_cstr; if (::strstr(response_cstr, "qXfer:auxv:read+")) @@ -377,9 +384,12 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { m_supports_qXfer_libraries_read = eLazyBoolYes; if (::strstr(response_cstr, "qXfer:features:read+")) m_supports_qXfer_features_read = eLazyBoolYes; + if (::strstr(response_cstr, "qXfer:memory-map:read+")) + m_supports_qXfer_memory_map_read = eLazyBoolYes; // Look for a list of compressions in the features list e.g. - // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma + // 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 = @@ -543,9 +553,8 @@ GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); } -// Check if the target supports 'p' packet. It sends out a 'p' -// packet and checks the response. A normal packet will tell us -// that support is available. +// Check if the target supports 'p' packet. It sends out a 'p' packet and +// checks the response. A normal packet will tell us that support is available. // // Takes a valid thread ID because p needs to apply to a thread. bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { @@ -600,8 +609,8 @@ bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { void GDBRemoteCommunicationClient::EnableErrorStringInPacket() { if (m_supports_error_string_reply == eLazyBoolCalculate) { StringExtractorGDBRemote response; - // We try to enable error strings in remote packets - // but if we fail, we just work in the older way. + // We try to enable error strings in remote packets but if we fail, we just + // work in the older way. m_supports_error_string_reply = eLazyBoolNo; if (SendPacketAndWaitForResponse("QEnableErrorStrings", response, false) == PacketResult::Success) { @@ -715,12 +724,10 @@ lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { 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. + // 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) { @@ -775,8 +782,8 @@ bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { 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... + // 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; @@ -822,6 +829,15 @@ int GDBRemoteCommunicationClient::SendArgumentsPacket( return -1; } +int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) { + for (const auto &KV : env) { + int r = SendEnvironmentPacket(Environment::compose(KV).c_str()); + if (r != 0) + return r; + } + return 0; +} + int GDBRemoteCommunicationClient::SendEnvironmentPacket( char const *name_equal_value) { if (name_equal_value && name_equal_value[0]) { @@ -925,18 +941,9 @@ int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( 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; - } - } - return false; +llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() { + GetHostInfo(); + return m_os_version; } bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { @@ -1199,9 +1206,7 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { // "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) + if (!m_os_version.tryParse(value)) ++num_keys_decoded; } else if (name.equals("watchpoint_exceptions_received")) { m_watchpoints_trigger_after_instruction = @@ -1451,7 +1456,8 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == - PacketResult::Success) { + PacketResult::Success && + response.GetResponseType() == StringExtractorGDBRemote::eResponse) { llvm::StringRef name; llvm::StringRef value; addr_t addr_value = LLDB_INVALID_ADDRESS; @@ -1527,8 +1533,134 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( if (m_supports_memory_region_info == eLazyBoolNo) { error.SetErrorString("qMemoryRegionInfo is not supported"); } - if (error.Fail()) - region_info.Clear(); + + // Try qXfer:memory-map:read to get region information not included in + // qMemoryRegionInfo + MemoryRegionInfo qXfer_region_info; + Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info); + + if (error.Fail()) { + // If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded, use + // the qXfer result as a fallback + if (qXfer_error.Success()) { + region_info = qXfer_region_info; + error.Clear(); + } else { + region_info.Clear(); + } + } else if (qXfer_error.Success()) { + // If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if + // both regions are the same range, update the result to include the flash- + // memory information that is specific to the qXfer result. + if (region_info.GetRange() == qXfer_region_info.GetRange()) { + region_info.SetFlash(qXfer_region_info.GetFlash()); + region_info.SetBlocksize(qXfer_region_info.GetBlocksize()); + } + } + return error; +} + +Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( + lldb::addr_t addr, MemoryRegionInfo ®ion) { + Status error = LoadQXferMemoryMap(); + if (!error.Success()) + return error; + for (const auto &map_region : m_qXfer_memory_map) { + if (map_region.GetRange().Contains(addr)) { + region = map_region; + return error; + } + } + error.SetErrorString("Region not found"); + return error; +} + +Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() { + + Status error; + + if (m_qXfer_memory_map_loaded) + // Already loaded, return success + return error; + + if (!XMLDocument::XMLEnabled()) { + error.SetErrorString("XML is not supported"); + return error; + } + + if (!GetQXferMemoryMapReadSupported()) { + error.SetErrorString("Memory map is not supported"); + return error; + } + + std::string xml; + lldb_private::Status lldberr; + if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml, + lldberr)) { + error.SetErrorString("Failed to read memory map"); + return error; + } + + XMLDocument xml_document; + + if (!xml_document.ParseMemory(xml.c_str(), xml.size())) { + error.SetErrorString("Failed to parse memory map xml"); + return error; + } + + XMLNode map_node = xml_document.GetRootElement("memory-map"); + if (!map_node) { + error.SetErrorString("Invalid root node in memory map xml"); + return error; + } + + m_qXfer_memory_map.clear(); + + map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool { + if (!memory_node.IsElement()) + return true; + if (memory_node.GetName() != "memory") + return true; + auto type = memory_node.GetAttributeValue("type", ""); + uint64_t start; + uint64_t length; + if (!memory_node.GetAttributeValueAsUnsigned("start", start)) + return true; + if (!memory_node.GetAttributeValueAsUnsigned("length", length)) + return true; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(start); + region.GetRange().SetByteSize(length); + if (type == "rom") { + region.SetReadable(MemoryRegionInfo::eYes); + this->m_qXfer_memory_map.push_back(region); + } else if (type == "ram") { + region.SetReadable(MemoryRegionInfo::eYes); + region.SetWritable(MemoryRegionInfo::eYes); + this->m_qXfer_memory_map.push_back(region); + } else if (type == "flash") { + region.SetFlash(MemoryRegionInfo::eYes); + memory_node.ForEachChildElement( + [®ion](const XMLNode &prop_node) -> bool { + if (!prop_node.IsElement()) + return true; + if (prop_node.GetName() != "property") + return true; + auto propname = prop_node.GetAttributeValue("name", ""); + if (propname == "blocksize") { + uint64_t blocksize; + if (prop_node.GetElementTextAsUnsigned(blocksize)) + region.SetBlocksize(blocksize); + } + return true; + }); + this->m_qXfer_memory_map.push_back(region); + } + return true; + }); + + m_qXfer_memory_map_loaded = true; + return error; } @@ -1585,13 +1717,13 @@ GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction( Status 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 + // 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 and ppc64le, watchpoint exceptions are always - // generated before the instruction is executed. The connected target - // may not support qHostInfo or qWatchpointSupportInfo packets. + // 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 || atype == llvm::Triple::ppc64le) @@ -1789,11 +1921,12 @@ bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( 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 + // The process name from ASCII hex bytes since we can't control the + // characters in a process name std::string name; extractor.GetHexByteString(name); - process_info.GetExecutableFile().SetFile(name, false); + process_info.GetExecutableFile().SetFile(name, false, + FileSpec::Style::native); } else if (name.equals("cputype")) { value.getAsInteger(0, cpu); } else if (name.equals("cpusubtype")) { @@ -2046,8 +2179,8 @@ uint32_t GDBRemoteCommunicationClient::FindProcesses( } } StringExtractorGDBRemote response; - // Increase timeout as the first qfProcessInfo packet takes a long time - // on Android. The value of 1min was arrived at empirically. + // 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) { @@ -2081,8 +2214,8 @@ bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, 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.. + // 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; @@ -2108,8 +2241,8 @@ bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, 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.. + // 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; @@ -2335,8 +2468,8 @@ bool GDBRemoteCommunicationClient::LaunchGDBServer( // 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 + // Make the GDB server we launch accept connections from any host since + // we can't figure out the hostname stream.Printf("host:*;"); } } @@ -2650,13 +2783,16 @@ lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( // 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 -{ + const Timeout<std::micro> &timeout) { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); + uint32_t timeout_sec = UINT32_MAX; + if (timeout) { + // TODO: Use chrono version of std::ceil once c++17 is available. + timeout_sec = std::ceil(std::chrono::duration<double>(*timeout).count()); + } stream.PutHex32(timeout_sec); if (working_dir) { std::string path{working_dir.GetPath(false)}; @@ -3125,9 +3261,8 @@ bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, 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 + // QSaveRegisterState and QRestoreRegisterState packets must both be + // supported in order to be useful if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; @@ -3406,7 +3541,7 @@ bool GDBRemoteCommunicationClient::GetModuleInfo( StringExtractor extractor(value); std::string uuid; extractor.GetHexByteString(uuid); - module_spec.GetUUID().SetFromCString(uuid.c_str(), uuid.size() / 2); + module_spec.GetUUID().SetFromStringRef(uuid, uuid.size() / 2); } else if (name == "triple") { StringExtractor extractor(value); std::string triple; @@ -3523,8 +3658,8 @@ GDBRemoteCommunicationClient::GetModulesInfo( // query the target remote for extended information using the qXfer packet // -// example: object='features', annex='target.xml', out=<xml output> -// return: 'true' on success +// 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, @@ -3631,10 +3766,9 @@ bool GDBRemoteCommunicationClient::ReadExtFeature( 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. + // 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? @@ -3659,8 +3793,8 @@ void GDBRemoteCommunicationClient::ServeSymbolLookups( first_qsymbol_query = false; if (response.IsUnsupportedResponse()) { - // qSymbol is not supported by the current GDB server we are connected - // to + // qSymbol is not supported by the current GDB server we are + // connected to m_supports_qSymbol = false; return; } else { @@ -3725,10 +3859,8 @@ void GDBRemoteCommunicationClient::ServeSymbolLookups( } } // 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. + // 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(); @@ -3763,8 +3895,7 @@ void GDBRemoteCommunicationClient::ServeSymbolLookups( StructuredData::Array * GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { if (!m_supported_async_json_packets_is_valid) { - // Query the server for the array of supported asynchronous JSON - // packets. + // 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)); @@ -3778,8 +3909,8 @@ GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { 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. + // We were returned something other than a JSON array. This is + // invalid. Clear it out. if (log) log->Printf("GDBRemoteCommunicationClient::%s(): " "QSupportedAsyncJSONPackets returned invalid " @@ -3837,8 +3968,7 @@ Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( return error; } - // Build command: Configure{type_name}: serialized config - // data. + // Build command: Configure{type_name}: serialized config data. StreamGDBRemote stream; stream.PutCString("QConfigure"); stream.PutCString(type_name.AsCString()); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index ba67b82463987..cf1d249768d7a 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -121,6 +121,7 @@ public: /// response was received. //------------------------------------------------------------------ int SendEnvironmentPacket(char const *name_equal_value); + int SendEnvironment(const Environment &env); int SendLaunchArchPacket(const char *arch); @@ -265,7 +266,7 @@ public: bool GetDefaultThreadId(lldb::tid_t &tid); - bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update); + llvm::VersionTuple GetOSVersion(); bool GetOSBuildString(std::string &s); @@ -354,6 +355,8 @@ public: bool GetQXferFeaturesReadSupported(); + bool GetQXferMemoryMapReadSupported(); + LazyBool SupportsAllocDeallocMemory() // const { // Uncomment this to have lldb pretend the debug server doesn't respond to @@ -401,8 +404,7 @@ public: // the process to exit std::string *command_output, // Pass nullptr if you don't want the command output - uint32_t timeout_sec); // Timeout in seconds to wait for shell program to - // finish + const Timeout<std::micro> &timeout); bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low); @@ -544,6 +546,7 @@ protected: LazyBool m_supports_qXfer_libraries_read; LazyBool m_supports_qXfer_libraries_svr4_read; LazyBool m_supports_qXfer_features_read; + LazyBool m_supports_qXfer_memory_map_read; LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_jThreadExtendedInfo; LazyBool m_supports_jLoadedDynamicLibrariesInfos; @@ -570,9 +573,7 @@ protected: ArchSpec m_host_arch; ArchSpec m_process_arch; - uint32_t m_os_version_major; - uint32_t m_os_version_minor; - uint32_t m_os_version_update; + llvm::VersionTuple m_os_version; std::string m_os_build; std::string m_os_kernel; std::string m_hostname; @@ -587,6 +588,9 @@ protected: bool m_supported_async_json_packets_is_valid; lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp; + std::vector<MemoryRegionInfo> m_qXfer_memory_map; + bool m_qXfer_memory_map_loaded; + bool GetCurrentProcessInfo(bool allow_lazy_pid = true); bool GetGDBServerVersion(); @@ -609,6 +613,11 @@ protected: llvm::MutableArrayRef<uint8_t> &buffer, size_t offset); + Status LoadQXferMemoryMap(); + + Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr, + MemoryRegionInfo ®ion); + private: DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient); }; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 4be92b79fd1a9..4fc1fc7a1964e 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -19,8 +19,8 @@ // Project includes #include "ProcessGDBRemoteLog.h" -#include "Utility/StringExtractorGDBRemote.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index a35352480040b..880caacd64149 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -57,8 +57,8 @@ protected: bool m_exit_now; // use in asynchronous handling to indicate process should // exit. - bool m_send_error_strings; // If the client enables this then - // we will send error strings as well. + bool m_send_error_strings = false; // If the client enables this then + // we will send error strings as well. PacketResult Handle_QErrorStringEnable(StringExtractorGDBRemote &packet); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp index 3cf6b8ac07b24..c5b478378faa4 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -28,7 +28,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/Platform.h" @@ -36,13 +36,14 @@ #include "lldb/Utility/Endian.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/SafeMachO.h" #include "lldb/Utility/StreamGDBRemote.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/Triple.h" // Project includes #include "ProcessGDBRemoteLog.h" -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" #ifdef __ANDROID__ #include "lldb/Host/android/HostInfoAndroid.h" @@ -218,12 +219,15 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( if (sub != LLDB_INVALID_CPUTYPE) response.Printf("cpusubtype:%u;", sub); - if (cpu == ArchSpec::kCore_arm_any) { + if (cpu == llvm::MachO::CPU_TYPE_ARM + || cpu == llvm::MachO::CPU_TYPE_ARM64) { // Indicate the OS type. #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 response.PutCString("ostype:tvos;"); #elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 response.PutCString("ostype:watchos;"); +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 + response.PutCString("ostype:bridgeos;"); #else response.PutCString("ostype:ios;"); #endif @@ -265,19 +269,10 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( break; } - uint32_t major = UINT32_MAX; - uint32_t minor = UINT32_MAX; - uint32_t update = UINT32_MAX; - if (HostInfo::GetOSVersion(major, minor, update)) { - if (major != UINT32_MAX) { - response.Printf("os_version:%u", major); - if (minor != UINT32_MAX) { - response.Printf(".%u", minor); - if (update != UINT32_MAX) - response.Printf(".%u", update); - } - response.PutChar(';'); - } + llvm::VersionTuple version = HostInfo::GetOSVersion(); + if (!version.empty()) { + response.Format("os_version:{0}", version.getAsString()); + response.PutChar(';'); } std::string s; @@ -295,9 +290,9 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( #if defined(__APPLE__) #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - // For iOS devices, we are connected through a USB Mux so we never pretend - // to actually have a hostname as far as the remote lldb that is connecting - // to this lldb-platform is concerned + // For iOS devices, we are connected through a USB Mux so we never pretend to + // actually have a hostname as far as the remote lldb that is connecting to + // this lldb-platform is concerned response.PutCString("hostname:"); response.PutCStringAsRawHex8("127.0.0.1"); response.PutChar(';'); @@ -357,7 +352,8 @@ GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo( StringExtractor extractor(value); std::string file; extractor.GetHexByteString(file); - match_info.GetProcessInfo().GetExecutableFile().SetFile(file, false); + match_info.GetProcessInfo().GetExecutableFile().SetFile( + file, false, FileSpec::Style::native); } else if (key.equals("name_match")) { NameMatch name_match = llvm::StringSwitch<NameMatch>(value) .Case("equals", NameMatch::Equals) @@ -401,7 +397,7 @@ GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo( match_info.GetProcessInfo().SetEffectiveGroupID(gid); } else if (key.equals("all_users")) { match_info.SetMatchAllUsers( - Args::StringToBoolean(value, false, &success)); + OptionArgParser::ToBoolean(value, false, &success)); } else if (key.equals("triple")) { match_info.GetProcessInfo().GetArchitecture() = HostInfo::GetAugmentedArchSpec(value); @@ -415,8 +411,8 @@ GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo( } if (Host::FindProcesses(match_info, m_proc_infos)) { - // We found something, return the first item by calling the get - // subsequent process info packet handler... + // We found something, return the first item by calling the get subsequent + // process info packet handler... return Handle_qsProcessInfo(packet); } return SendErrorResponse(3); @@ -731,14 +727,13 @@ GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( if (packet.GetChar() == ',') { // FIXME: add timeout to qPlatform_shell packet // uint32_t timeout = packet.GetHexMaxU32(false, 32); - uint32_t timeout = 10; if (packet.GetChar() == ',') packet.GetHexByteString(working_dir); int status, signo; std::string output; - Status err = - Host::RunShellCommand(path.c_str(), FileSpec{working_dir, true}, - &status, &signo, &output, timeout); + Status err = Host::RunShellCommand( + path.c_str(), FileSpec{working_dir, true}, &status, &signo, &output, + std::chrono::seconds(10)); StreamGDBRemote response; if (err.Fail()) { response.PutCString("F,"); @@ -945,8 +940,7 @@ GDBRemoteCommunicationServerCommon::Handle_QEnvironment( packet.SetFilePos(::strlen("QEnvironment:")); const uint32_t bytes_left = packet.GetBytesLeft(); if (bytes_left > 0) { - m_process_launch_info.GetEnvironmentEntries().AppendArgument( - llvm::StringRef::withNullAsEmpty(packet.Peek())); + m_process_launch_info.GetEnvironment().insert(packet.Peek()); return SendOKResponse(); } return SendErrorResponse(12); @@ -960,7 +954,7 @@ GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded( if (bytes_left > 0) { std::string str; packet.GetHexByteString(str); - m_process_launch_info.GetEnvironmentEntries().AppendArgument(str); + m_process_launch_info.GetEnvironment().insert(str); return SendOKResponse(); } return SendErrorResponse(12); @@ -981,11 +975,11 @@ GDBRemoteCommunicationServerCommon::Handle_QLaunchArch( GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { - // The 'A' packet is the most over designed packet ever here with - // redundant argument indexes, redundant argument lengths and needed hex - // encoded argument string values. Really all that is needed is a comma - // separated hex encoded argument value list, but we will stay true to the - // documented version of the 'A' packet here... + // The 'A' packet is the most over designed packet ever here with redundant + // argument indexes, redundant argument lengths and needed hex encoded + // argument string values. Really all that is needed is a comma separated hex + // encoded argument value list, but we will stay true to the documented + // version of the 'A' packet here... Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); int actual_arg_index = 0; @@ -993,8 +987,8 @@ GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { packet.SetFilePos(1); // Skip the 'A' bool success = true; while (success && packet.GetBytesLeft() > 0) { - // Decode the decimal argument string length. This length is the - // number of hex nibbles in the argument string value. + // Decode the decimal argument string length. This length is the number of + // hex nibbles in the argument string value. const uint32_t arg_len = packet.GetU32(UINT32_MAX); if (arg_len == UINT32_MAX) success = false; @@ -1003,8 +997,8 @@ GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { if (packet.GetChar() != ',') success = false; else { - // Decode the argument index. We ignore this really because - // who would really send down the arguments in a random order??? + // Decode the argument index. We ignore this really because who would + // really send down the arguments in a random order??? const uint32_t arg_idx = packet.GetU32(UINT32_MAX); if (arg_idx == UINT32_MAX) success = false; @@ -1013,9 +1007,9 @@ GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { if (packet.GetChar() != ',') success = false; else { - // Decode the argument string value from hex bytes - // back into a UTF8 string and make sure the length - // matches the one supplied in the packet + // Decode the argument string value from hex bytes back into a UTF8 + // string and make sure the length matches the one supplied in the + // packet std::string arg; if (packet.GetHexByteStringFixedLength(arg, arg_len) != (arg_len / 2)) @@ -1029,7 +1023,8 @@ GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { if (success) { if (arg_idx == 0) - m_process_launch_info.GetExecutableFile().SetFile(arg, false); + m_process_launch_info.GetExecutableFile().SetFile( + arg, false, FileSpec::Style::native); m_process_launch_info.GetArguments().AppendArgument(arg); if (log) log->Printf("LLGSPacketHandler::%s added arg %d: \"%s\"", @@ -1254,8 +1249,8 @@ void GDBRemoteCommunicationServerCommon:: // Nothing. break; } - // In case of MIPS64, pointer size is depend on ELF ABI - // For N32 the pointer size is 4 and for N64 it is 8 + // In case of MIPS64, pointer size is depend on ELF ABI For N32 the pointer + // size is 4 and for N64 it is 8 std::string abi = proc_arch.GetTargetABI(); if (!abi.empty()) response.Printf("elf_abi:%s;", abi.c_str()); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 32741c2404e22..50392fa389562 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -33,9 +33,9 @@ #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeThreadProtocol.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/JSON.h" @@ -49,7 +49,7 @@ // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; @@ -236,16 +236,15 @@ Status GDBRemoteCommunicationServerLLGS::LaunchProcess() { m_debugged_process_up = std::move(*process_or); } - // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol - // as needed. - // llgs local-process debugging may specify PTY paths, which will make these - // file actions non-null - // process launch -i/e/o will also make these file actions non-null - // nullptr means that the traffic is expected to flow over gdb-remote protocol + // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as + // needed. llgs local-process debugging may specify PTY paths, which will + // make these file actions non-null process launch -i/e/o will also make + // these file actions non-null nullptr means that the traffic is expected to + // flow over gdb-remote protocol if (should_forward_stdio) { // nullptr means it's not redirected to file or pty (in case of LLGS local) - // at least one of stdio will be transferred pty<->gdb-remote - // we need to give the pty master handle to this object to read and/or write + // at least one of stdio will be transferred pty<->gdb-remote we need to + // give the pty master handle to this object to read and/or write LLDB_LOG(log, "pid = {0}: setting up stdout/stderr redirection via $O " "gdb-remote commands", @@ -291,7 +290,7 @@ Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) { // else. if (m_debugged_process_up && m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID) - return Status("cannot attach to a process %" PRIu64 + return Status("cannot attach to process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, m_debugged_process_up->GetID()); @@ -410,8 +409,8 @@ static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) { JSONObject::SP register_object_sp = std::make_shared<JSONObject>(); #ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET - // Expedite all registers in the first register set (i.e. should be GPRs) that - // are not contained in other registers. + // Expedite all registers in the first register set (i.e. should be GPRs) + // that are not contained in other registers. const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0); if (!reg_set_p) return nullptr; @@ -420,8 +419,7 @@ static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) { uint32_t reg_num = *reg_num_p; #else // Expedite only a couple of registers until we figure out why sending - // registers is - // expensive. + // registers is expensive. static const uint32_t k_expedited_registers[] = { LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM}; @@ -595,8 +593,7 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( return SendErrorResponse(52); // FIXME implement register handling for exec'd inferiors. - // if (tid_stop_info.reason == eStopReasonExec) - // { + // if (tid_stop_info.reason == eStopReasonExec) { // const bool force = true; // InitializeRegisters(force); // } @@ -633,14 +630,14 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( response.PutChar(';'); } - // If a 'QListThreadsInStopReply' was sent to enable this feature, we - // will send all thread IDs back in the "threads" key whose value is - // a list of hex thread IDs separated by commas: + // If a 'QListThreadsInStopReply' was sent to enable this feature, we will + // send all thread IDs back in the "threads" key whose value is a list of hex + // thread IDs separated by commas: // "threads:10a,10b,10c;" - // This will save the debugger from having to send a pair of qfThreadInfo - // and qsThreadInfo packets, but it also might take a lot of room in the - // stop reply packet, so it must be enabled only on systems where there - // are no limits on packet lengths. + // This will save the debugger from having to send a pair of qfThreadInfo and + // qsThreadInfo packets, but it also might take a lot of room in the stop + // reply packet, so it must be enabled only on systems where there are no + // limits on packet lengths. if (m_list_threads_in_stop_reply) { response.PutCString("threads:"); @@ -655,12 +652,11 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( } response.PutChar(';'); - // Include JSON info that describes the stop reason for any threads - // that actually have stop reasons. We use the new "jstopinfo" key - // whose values is hex ascii JSON that contains the thread IDs - // thread stop info only for threads that have stop reasons. Only send - // this if we have more than one thread otherwise this packet has all - // the info it needs. + // Include JSON info that describes the stop reason for any threads that + // actually have stop reasons. We use the new "jstopinfo" key whose values + // is hex ascii JSON that contains the thread IDs thread stop info only for + // threads that have stop reasons. Only send this if we have more than one + // thread otherwise this packet has all the info it needs. if (thread_index > 0) { const bool threads_with_valid_stop_info_only = true; JSONArray::SP threads_info_sp = GetJSONThreadsInfo( @@ -806,8 +802,8 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( __FUNCTION__, process->GetID()); } - // Close the pipe to the inferior terminal i/o if we launched it - // and set one up. + // Close the pipe to the inferior terminal i/o if we launched it and set one + // up. MaybeCloseInferiorTerminalConnection(); // We are ready to exit the debug monitor. @@ -823,8 +819,7 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( if (log) log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); - // Send the stop reason unless this is the stop after the - // launch or attach. + // Send the stop reason unless this is the stop after the launch or attach. switch (m_inferior_prev_state) { case eStateLaunching: case eStateAttaching: @@ -859,13 +854,11 @@ void GDBRemoteCommunicationServerLLGS::ProcessStateChanged( break; case StateType::eStateStopped: - // Make sure we get all of the pending stdout/stderr from the inferior - // and send it to the lldb host before we send the state change - // notification + // Make sure we get all of the pending stdout/stderr from the inferior and + // send it to the lldb host before we send the state change notification SendProcessOutput(); // Then stop the forwarding, so that any late output (see llvm.org/pr25652) - // does not - // interfere with our protocol. + // does not interfere with our protocol. StopSTDIOForwarding(); HandleInferiorState_Stopped(process); break; @@ -1287,8 +1280,8 @@ GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) { (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) return SendErrorResponse(68); - // Make sure we set the current thread so g and p packets return - // the data the gdb will expect. + // Make sure we set the current thread so g and p packets return the data the + // gdb will expect. lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); SetCurrentThreadID(tid); @@ -1397,10 +1390,9 @@ GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) { Status error; // We have two branches: what to do if a continue thread is specified (in - // which case we target - // sending the signal to that thread), or when we don't have a continue thread - // set (in which - // case we send a signal to the process). + // which case we target sending the signal to that thread), or when we don't + // have a continue thread set (in which case we send a signal to the + // process). // TODO discuss with Greg Clayton, make sure this makes sense. @@ -1639,8 +1631,8 @@ GDBRemoteCommunicationServerLLGS::SendStopReasonForState( case eStateStopped: case eStateCrashed: { lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); - // Make sure we set the current thread so g and p packets return - // the data the gdb will expect. + // Make sure we set the current thread so g and p packets return the data + // the gdb will expect. SetCurrentThreadID(tid); return SendStopReplyPacketForThread(tid); } @@ -2043,9 +2035,8 @@ GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) { return SendErrorResponse(0x47); } - // The dwarf expression are evaluate on host site - // which may cause register size to change - // Hence the reg_size may not be same as reg_info->bytes_size + // The dwarf expression are evaluate on host site which may cause register + // size to change Hence the reg_size may not be same as reg_info->bytes_size if ((reg_size != reg_info->byte_size) && !(reg_info->dynamic_size_dwarf_expr_bytes)) { return SendIllFormedResponse(packet, "P packet register size is incorrect"); @@ -2376,10 +2367,9 @@ GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported( Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Currently only the NativeProcessProtocol knows if it can handle a - // qMemoryRegionInfoSupported - // request, but we're not guaranteed to be attached to a process. For now - // we'll assume the - // client only asks this when a process is being debugged. + // qMemoryRegionInfoSupported request, but we're not guaranteed to be + // attached to a process. For now we'll assume the client only asks this + // when a process is being debugged. // Ensure we have a process running; otherwise, we can't figure this out // since we won't have a NativeProcessProtocol. @@ -2670,8 +2660,7 @@ GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { } // We first try to use a continue thread id. If any one or any all set, use - // the current thread. - // Bail out if we don't have a thread id. + // the current thread. Bail out if we don't have a thread id. lldb::tid_t tid = GetContinueThreadID(); if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) tid = GetCurrentThreadID(); @@ -2935,7 +2924,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vAttach( log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to " "pid %" PRIu64 ": %s\n", __FUNCTION__, pid, error.AsCString()); - return SendErrorResponse(0x01); + return SendErrorResponse(error); } // Notify we attached by sending a stop packet. @@ -3093,8 +3082,8 @@ GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( std::vector<int> signals; packet.SetFilePos(strlen("QPassSignals:")); - // Read sequence of hex signal numbers divided by a semicolon and - // optionally spaces. + // Read sequence of hex signal numbers divided by a semicolon and optionally + // spaces. while (packet.GetBytesLeft() > 0) { int signal = packet.GetS32(-1, 16); if (signal < 0) @@ -3154,8 +3143,7 @@ NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( return nullptr; // If the client hasn't asked for thread suffix support, there will not be a - // thread suffix. - // Use the current thread in that case. + // thread suffix. Use the current thread in that case. if (!m_thread_suffix_supported) { const lldb::tid_t current_tid = GetCurrentThreadID(); if (current_tid == LLDB_INVALID_THREAD_ID) @@ -3201,9 +3189,9 @@ NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const { if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) { - // Use whatever the debug process says is the current thread id - // since the protocol either didn't specify or specified we want - // any/all threads marked as the current thread. + // Use whatever the debug process says is the current thread id since the + // protocol either didn't specify or specified we want any/all threads + // marked as the current thread. if (!m_debugged_process_up) return LLDB_INVALID_THREAD_ID; return m_debugged_process_up->GetCurrentThreadID(); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp index 04ed9d704e137..26e28a900320a 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -14,6 +14,7 @@ // C Includes // C++ Includes #include <chrono> +#include <csignal> #include <cstring> #include <mutex> #include <sstream> @@ -38,7 +39,7 @@ #include "lldb/Utility/UriParser.h" // Project includes -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; @@ -101,11 +102,11 @@ Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( if (port == UINT16_MAX) port = GetNextAvailablePort(); - // Spawn a new thread to accept the port that gets bound after - // binding to port 0 (zero). + // Spawn a new thread to accept the port that gets bound after binding to + // port 0 (zero). - // ignore the hostname send from the remote end, just use the ip address - // that we're currently communicating with as the hostname + // ignore the hostname send from the remote end, just use the ip address that + // we're currently communicating with as the hostname // Spawn a debugserver and try to get the port it listens to. ProcessLaunchInfo debugserver_launch_info; @@ -116,8 +117,8 @@ Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( if (log) log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port); - // Do not run in a new session so that it can not linger after the - // platform closes. + // Do not run in a new session so that it can not linger after the platform + // closes. debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); debugserver_launch_info.SetMonitorProcessCallback( std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, @@ -170,8 +171,8 @@ GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( #ifdef _WIN32 return SendErrorResponse(9); #else - // Spawn a local debugserver as a platform so we can then attach or launch - // a process... + // Spawn a local debugserver as a platform so we can then attach or launch a + // process... Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) @@ -259,8 +260,7 @@ GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); - // verify that we know anything about this pid. - // Scope for locker + // verify that we know anything about this pid. Scope for locker { std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { @@ -306,8 +306,8 @@ bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { return true; } - // the launched process still lives. Now try killing it again, - // this time with an unblockable signal. + // the launched process still lives. Now try killing it again, this time + // with an unblockable signal. Host::Kill(pid, SIGKILL); for (size_t i = 0; i < 10; ++i) { @@ -321,8 +321,7 @@ bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { usleep(10000); } - // check one more time after the final usleep - // Scope for locker + // check one more time after the final usleep Scope for locker { std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) @@ -389,14 +388,13 @@ GDBRemoteCommunicationServerPlatform::Handle_qC( StreamString response; response.Printf("QC%" PRIx64, pid); - // If we launch a process and this GDB server is acting as a platform, - // then we need to clear the process launch state so we can start - // launching another process. In order to launch a process a bunch or - // packets need to be sent: environment packets, working directory, - // disable ASLR, and many more settings. When we launch a process we - // then need to know when to clear this information. Currently we are - // selecting the 'qC' packet as that packet which seems to make the most - // sense. + // If we launch a process and this GDB server is acting as a platform, then + // we need to clear the process launch state so we can start launching + // another process. In order to launch a process a bunch or packets need to + // be sent: environment packets, working directory, disable ASLR, and many + // more settings. When we launch a process we then need to know when to clear + // this information. Currently we are selecting the 'qC' packet as that + // packet which seems to make the most sense. if (pid != LLDB_INVALID_PROCESS_ID) { m_process_launch_info.Clear(); } @@ -445,9 +443,8 @@ Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { return Status("%s: no process command line specified to launch", __FUNCTION__); - // specify the process monitor if not already set. This should - // generally be what happens since we need to reap started - // processes. + // specify the process monitor if not already set. This should generally be + // what happens since we need to reap started processes. if (!m_process_launch_info.GetMonitorProcessCallback()) m_process_launch_info.SetMonitorProcessCallback( std::bind( @@ -466,8 +463,8 @@ Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { m_process_launch_info.GetArguments().GetArgumentAtIndex(0), m_process_launch_info.GetProcessID()); - // add to list of spawned processes. On an lldb-gdbserver, we - // would expect there to be only one. + // add to list of spawned processes. On an lldb-gdbserver, we would expect + // there to be only one. const auto pid = m_process_launch_info.GetProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) { // add to spawned pids @@ -537,7 +534,7 @@ const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { if (domainsocket_dir_env != nullptr) g_domainsocket_dir = FileSpec(domainsocket_dir_env, false); else - HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, g_domainsocket_dir); + g_domainsocket_dir = HostInfo::GetProcessTempDir(); }); return g_domainsocket_dir; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp index e418deee01f3d..07dab751f4b90 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -25,7 +25,7 @@ #include "ThreadGDBRemote.h" #include "Utility/ARM_DWARF_Registers.h" #include "Utility/ARM_ehframe_Registers.h" -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; @@ -39,9 +39,9 @@ GDBRemoteRegisterContext::GDBRemoteRegisterContext( GDBRemoteDynamicRegisterInfo ®_info, bool read_all_at_once) : RegisterContext(thread, concrete_frame_idx), m_reg_info(reg_info), m_reg_valid(), m_reg_data(), m_read_all_at_once(read_all_at_once) { - // Resize our vector of bools to contain one bool for every register. - // We will use these boolean values to know when a register value - // is valid in m_reg_data. + // Resize our vector of bools to contain one bool for every register. We will + // use these boolean values to know when a register value is valid in + // m_reg_data. m_reg_valid.resize(reg_info.GetNumRegisters()); // Make a heap based buffer that is big enough to store all registers @@ -119,8 +119,8 @@ bool GDBRemoteRegisterContext::PrivateSetRegisterValue( if (success) { SetRegisterIsValid(reg, true); } else if (data.size() > 0) { - // Only set register is valid to false if we copied some bytes, else - // leave it as it was. + // Only set register is valid to false if we copied some bytes, else leave + // it as it was. SetRegisterIsValid(reg, false); } return success; @@ -133,11 +133,9 @@ bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg, return false; // Early in process startup, we can get a thread that has an invalid byte - // order - // because the process hasn't been completely set up yet (see the ctor where - // the - // byte order is setfrom the process). If that's the case, we can't set the - // value here. + // order because the process hasn't been completely set up yet (see the ctor + // where the byte order is setfrom the process). If that's the case, we + // can't set the value here. if (m_reg_data.GetByteOrder() == eByteOrderInvalid) { return false; } @@ -149,8 +147,7 @@ bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg, DataExtractor data(buffer_sp, endian::InlHostByteOrder(), sizeof(void *)); // If our register context and our register info disagree, which should never - // happen, don't - // overwrite past the end of the buffer. + // happen, don't overwrite past the end of the buffer. if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) return false; @@ -219,8 +216,7 @@ bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info, } if (reg_info->value_regs) { // Process this composite register request by delegating to the - // constituent - // primordial registers. + // constituent primordial registers. // Index of the primordial register. bool success = true; @@ -228,8 +224,8 @@ bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info, const uint32_t prim_reg = reg_info->value_regs[idx]; if (prim_reg == LLDB_INVALID_REGNUM) break; - // We have a valid primordial register as our constituent. - // Grab the corresponding register info. + // We have a valid primordial register as our constituent. Grab the + // corresponding register info. const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg); if (prim_reg_info == NULL) success = false; @@ -242,8 +238,7 @@ bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info, if (success) { // If we reach this point, all primordial register requests have - // succeeded. - // Validate this composite register. + // succeeded. Validate this composite register. SetRegisterIsValid(reg_info, true); } } else { @@ -262,16 +257,14 @@ bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info, reg_info->byte_offset + reg_info->byte_size); #endif // If our register context and our register info disagree, which should - // never happen, don't - // read past the end of the buffer. + // never happen, don't read past the end of the buffer. if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) return false; - // If we aren't extracting into our own buffer (which - // only happens when this function is called from - // ReadRegisterValue(uint32_t, Scalar&)) then - // we transfer bytes from our buffer into the data - // buffer that was passed in + // If we aren't extracting into our own buffer (which only happens when + // this function is called from ReadRegisterValue(uint32_t, Scalar&)) then + // we transfer bytes from our buffer into the data buffer that was passed + // in data.SetByteOrder(m_reg_data.GetByteOrder()); data.SetData(m_reg_data, reg_info->byte_offset, reg_info->byte_size); @@ -321,8 +314,7 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, #endif // If our register context and our register info disagree, which should never - // happen, don't - // overwrite past the end of the buffer. + // happen, don't overwrite past the end of the buffer. if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) return false; @@ -358,12 +350,10 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, bool success = true; if (reg_info->value_regs) { - // This register is part of another register. In this case we read the - // actual - // register data for any "value_regs", and once all that data is read, - // we will - // have enough data in our register context bytes for the value of - // this register + // This register is part of another register. In this case we read + // the actual register data for any "value_regs", and once all that + // data is read, we will have enough data in our register context + // bytes for the value of this register // Invalidate this composite register first. @@ -371,8 +361,8 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, const uint32_t reg = reg_info->value_regs[idx]; if (reg == LLDB_INVALID_REGNUM) break; - // We have a valid primordial register as our constituent. - // Grab the corresponding register info. + // We have a valid primordial register as our constituent. Grab the + // corresponding register info. const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg); if (value_reg_info == NULL) success = false; @@ -385,8 +375,7 @@ bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, } // Check if writing this register will invalidate any other register - // values? - // If so, invalidate them + // values? If so, invalidate them if (reg_info->invalidate_regs) { for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0]; reg != LLDB_INVALID_REGNUM; @@ -548,26 +537,22 @@ bool GDBRemoteRegisterContext::WriteAllRegisterValues( return true; uint32_t num_restored = 0; - // We need to manually go through all of the registers and - // restore them manually + // We need to manually go through all of the registers and restore them + // manually DataExtractor restore_data(data_sp, m_reg_data.GetByteOrder(), m_reg_data.GetAddressByteSize()); const RegisterInfo *reg_info; - // The g packet contents may either include the slice registers (registers - // defined in - // terms of other registers, e.g. eax is a subset of rax) or not. The - // slice registers - // should NOT be in the g packet, but some implementations may incorrectly - // include them. + // The g packet contents may either include the slice registers + // (registers defined in terms of other registers, e.g. eax is a subset + // of rax) or not. The slice registers should NOT be in the g packet, + // but some implementations may incorrectly include them. // // If the slice registers are included in the packet, we must step over - // the slice registers - // when parsing the packet -- relying on the RegisterInfo byte_offset - // field would be incorrect. - // If the slice registers are not included, then using the byte_offset - // values into the + // the slice registers when parsing the packet -- relying on the + // RegisterInfo byte_offset field would be incorrect. If the slice + // registers are not included, then using the byte_offset values into the // data buffer is the best way to find individual register values. uint64_t size_including_slice_registers = 0; @@ -591,21 +576,17 @@ bool GDBRemoteRegisterContext::WriteAllRegisterValues( } else if (size_not_including_slice_registers == restore_data.GetByteSize()) { // The size of the packet is the same as concatenating all of the - // registers sequentially, - // skipping the slice registers + // registers sequentially, skipping the slice registers use_byte_offset_into_buffer = true; } else if (size_including_slice_registers == restore_data.GetByteSize()) { // The slice registers are present in the packet (when they shouldn't - // be). - // Don't try to use the RegisterInfo byte_offset into the restore_data, - // it will - // point to the wrong place. + // be). Don't try to use the RegisterInfo byte_offset into the + // restore_data, it will point to the wrong place. use_byte_offset_into_buffer = false; } else { // None of our expected sizes match the actual g packet data we're - // looking at. - // The most conservative approach here is to use the running total byte - // offset. + // looking at. The most conservative approach here is to use the + // running total byte offset. use_byte_offset_into_buffer = false; } @@ -664,11 +645,9 @@ bool GDBRemoteRegisterContext::WriteAllRegisterValues( if (reg_info->value_regs) // skip registers that are slices of real // registers continue; - // Skip the fpsr and fpcr floating point status/control register writing - // to - // work around a bug in an older version of debugserver that would lead - // to - // register context corruption when writing fpsr/fpcr. + // Skip the fpsr and fpcr floating point status/control register + // writing to work around a bug in an older version of debugserver that + // would lead to register context corruption when writing fpsr/fpcr. if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 || strcmp(reg_info->name, "fpcr") == 0)) { continue; @@ -752,8 +731,8 @@ void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { static uint32_t g_q14_regs[] = {71, 72, LLDB_INVALID_REGNUM}; // (d28, d29) static uint32_t g_q15_regs[] = {73, 74, LLDB_INVALID_REGNUM}; // (d30, d31) - // This is our array of composite registers, with each element coming from the - // above register mappings. + // This is our array of composite registers, with each element coming from + // the above register mappings. static uint32_t *g_composites[] = { g_d0_regs, g_d1_regs, g_d2_regs, g_d3_regs, g_d4_regs, g_d5_regs, g_d6_regs, g_d7_regs, g_d8_regs, g_d9_regs, g_d10_regs, g_d11_regs, @@ -884,21 +863,17 @@ void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { if (from_scratch) { // Calculate the offsets of the registers // Note that the layout of the "composite" registers (d0-d15 and q0-q15) - // which comes after the - // "primordial" registers is important. This enables us to calculate the - // offset of the composite - // register by using the offset of its first primordial register. For - // example, to calculate the - // offset of q0, use s0's offset. + // which comes after the "primordial" registers is important. This enables + // us to calculate the offset of the composite register by using the offset + // of its first primordial register. For example, to calculate the offset + // of q0, use s0's offset. if (g_register_infos[2].byte_offset == 0) { uint32_t byte_offset = 0; for (i = 0; i < num_registers; ++i) { // For primordial registers, increment the byte_offset by the byte_size - // to arrive at the - // byte_offset for the next register. Otherwise, we have a composite - // register whose - // offset can be calculated by consulting the offset of its first - // primordial register. + // to arrive at the byte_offset for the next register. Otherwise, we + // have a composite register whose offset can be calculated by + // consulting the offset of its first primordial register. if (!g_register_infos[i].value_regs) { g_register_infos[i].byte_offset = byte_offset; byte_offset += g_register_infos[i].byte_size; @@ -933,8 +908,8 @@ void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs; // First we need to validate that all registers that we already have match - // the non composite regs. - // If so, then we can add the registers, else we need to bail + // the non composite regs. If so, then we can add the registers, else we + // need to bail bool match = true; if (num_dynamic_regs == num_common_regs) { for (i = 0; match && i < num_dynamic_regs; ++i) { @@ -970,9 +945,8 @@ void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { // Find a matching primordial register info entry. if (reg_info && reg_info->name && ::strcasecmp(reg_info->name, reg_name) == 0) { - // The name matches the existing primordial entry. - // Find and assign the offset, and then add this composite - // register entry. + // The name matches the existing primordial entry. Find and + // assign the offset, and then add this composite register entry. g_comp_register_infos[i].byte_offset = reg_info->byte_offset; name.SetCString(g_comp_register_infos[i].name); AddRegister(g_comp_register_infos[i], name, alt_name, diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 35d02c15ab85b..b3d33b19bd666 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -24,6 +24,7 @@ // C++ Includes #include <algorithm> +#include <csignal> #include <map> #include <mutex> #include <sstream> @@ -46,11 +47,11 @@ #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" -#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" @@ -59,10 +60,12 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/Args.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" @@ -77,8 +80,8 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" -#include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Host.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Threading.h" @@ -91,11 +94,11 @@ using namespace lldb_private::process_gdb_remote; namespace lldb { // Provide a function that can easily dump the packet history if we know a -// ProcessGDBRemote * value (which we can get from logs or from debugging). -// We need the function in the lldb namespace so it makes it into the final +// ProcessGDBRemote * value (which we can get from logs or from debugging). We +// need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb -// namespace. This allows you to attach with a debugger and call this -// function and get the packet history dumped to a file. +// namespace. This allows you to attach with a debugger and call this function +// and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { StreamFile strm; Status error(strm.GetFile().Open(path, File::eOpenOptionWrite | @@ -158,8 +161,8 @@ static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { } // anonymous namespace end // TODO Randomly assigning a port is unsafe. We should get an unused -// ephemeral port from the kernel and make sure we reserve it before passing -// it to debugserver. +// ephemeral port from the kernel and make sure we reserve it before passing it +// to debugserver. #if defined(__APPLE__) #define LOW_PORT (IPPORT_RESERVED) @@ -233,8 +236,8 @@ bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, } return exe_module->GetFileSpec().Exists(); } - // However, if there is no executable module, we return true since we might be - // preparing to attach. + // However, if there is no executable module, we return true since we might + // be preparing to attach. return true; } @@ -256,7 +259,8 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, m_addr_to_mmap_size(), m_thread_create_bp_sp(), m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_command_sp(), m_breakpoint_pc_offset(0), - m_initial_tid(LLDB_INVALID_THREAD_ID) { + m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false), + m_erased_flash_ranges() { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, @@ -299,17 +303,16 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); - // We need to call finalize on the process before destroying ourselves - // to make sure all of the broadcaster cleanup goes as planned. If we - // destruct this class, then Process::~Process() might have problems - // trying to fully destroy the broadcaster. + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. Finalize(); - // The general Finalize is going to try to destroy the process and that SHOULD - // shut down the async thread. However, if we don't kill it it will get - // stranded and - // its connection will go away so when it wakes up it will crash. So kill it - // for sure here. + // The general Finalize is going to try to destroy the process and that + // SHOULD shut down the async thread. However, if we don't kill it it will + // get stranded and its connection will go away so when it wakes up it will + // crash. So kill it for sure here. StopAsyncThread(); KillDebugserverProcess(); } @@ -368,8 +371,7 @@ bool ProcessGDBRemote::ParsePythonTargetDefinition( } // If the remote stub didn't give us eh_frame or DWARF register numbers for a -// register, -// see if the ABI can provide them. +// register, see if the ABI can provide them. // DWARF and eh_frame register numbers are defined as a part of the ABI. static void AugmentRegisterInfoViaABI(RegisterInfo ®_info, ConstString reg_name, ABISP abi_sp) { @@ -422,9 +424,9 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { m_register_info.Clear(); - // Check if qHostInfo specified a specific packet timeout for this connection. - // If so then lets update our setting so the user knows what the timeout is - // and can see it. + // Check if qHostInfo specified a specific packet timeout for this + // connection. If so then lets update our setting so the user knows what the + // timeout is and can see it. const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout > std::chrono::seconds(0)) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); @@ -531,7 +533,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { reg_info.encoding = encoding; } else if (name.equals("format")) { Format format = eFormatInvalid; - if (Args::StringToFormat(value.str().c_str(), format, NULL) + if (OptionArgParser::ToFormat(value.str().c_str(), format, NULL) .Success()) reg_info.format = format; else { @@ -596,10 +598,8 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { } // We have to make a temporary ABI here, and not use the GetABI because - // this code - // gets called in DidAttach, when the target architecture (and - // consequently the ABI we'll get from - // the process) may be wrong. + // this code gets called in DidAttach, when the target architecture + // (and consequently the ABI we'll get from the process) may be wrong. ABISP abi_to_use = ABI::FindPlugin(shared_from_this(), arch_to_use); AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use); @@ -620,9 +620,9 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an - // updated debugserver down on the devices. - // On the other hand, if the accumulated reg_num is positive, see if we can - // add composite registers to the existing primordial ones. + // updated debugserver down on the devices. On the other hand, if the + // accumulated reg_num is positive, see if we can add composite registers to + // the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); if (!target_arch.IsValid()) { @@ -669,9 +669,8 @@ Status ProcessGDBRemote::DoConnectRemote(Stream *strm, lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid == LLDB_INVALID_PROCESS_ID) { - // We don't have a valid process ID, so note that we are connected - // and could now request to launch or attach, or get remote process - // listings... + // We don't have a valid process ID, so note that we are connected and + // could now request to launch or attach, or get remote process listings... SetPrivateState(eStateConnected); } else { // We have a valid process @@ -721,7 +720,8 @@ Status ProcessGDBRemote::DoConnectRemote(Stream *strm, if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { - // Prefer the *process'* architecture over that of the *host*, if available. + // Prefer the *process'* architecture over that of the *host*, if + // available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else @@ -800,8 +800,8 @@ Status ProcessGDBRemote::DoLaunch(Module *exe_module, const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { - // the inferior will be reading stdin from the specified file - // or stdio is completely disabled + // the inferior will be reading stdin from the specified file or stdio is + // completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; @@ -824,11 +824,14 @@ Status ProcessGDBRemote::DoLaunch(Module *exe_module, if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) - stdin_file_spec.SetFile(FileSystem::DEV_NULL, false); + stdin_file_spec.SetFile(FileSystem::DEV_NULL, false, + FileSpec::Style::native); if (!stdout_file_spec) - stdout_file_spec.SetFile(FileSystem::DEV_NULL, false); + stdout_file_spec.SetFile(FileSystem::DEV_NULL, false, + FileSpec::Style::native); if (!stderr_file_spec) - stderr_file_spec.SetFile(FileSystem::DEV_NULL, false); + stderr_file_spec.SetFile(FileSystem::DEV_NULL, false, + FileSpec::Style::native); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio @@ -888,16 +891,7 @@ Status ProcessGDBRemote::DoLaunch(Module *exe_module, } // Send the environment and the program + arguments after we connect - const Args &environment = launch_info.GetEnvironmentEntries(); - if (environment.GetArgumentCount()) { - size_t num_environment_entries = environment.GetArgumentCount(); - for (size_t i = 0; i < num_environment_entries; ++i) { - const char *env_entry = environment.GetArgumentAtIndex(i); - if (env_entry == NULL || - m_gdb_comm.SendEnvironmentPacket(env_entry) != 0) - break; - } - } + m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); { // Scope for the scoped timeout object @@ -1004,16 +998,15 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { return error; } - // Start the communications read thread so all incoming data can be - // parsed into packets and queued as they arrive. + // Start the communications read thread so all incoming data can be parsed + // into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); - // We always seem to be able to open a connection to a local port - // so we need to make sure we can then send data to it. If we can't - // then we aren't actually connected to anything, so try and do the - // handshake with the remote GDB server and make sure that goes - // alright. + // We always seem to be able to open a connection to a local port so we need + // to make sure we can then send data to it. If we can't then we aren't + // actually connected to anything, so try and do the handshake with the + // remote GDB server and make sure that goes alright. if (!m_gdb_comm.HandshakeWithServer(&error)) { m_gdb_comm.Disconnect(); if (error.Success()) @@ -1055,9 +1048,9 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { // See if the GDB server supports the qHostInfo information - // See if the GDB server supports the qProcessInfo packet, if so - // prefer that over the Host information as it will be more specific - // to our process. + // See if the GDB server supports the qProcessInfo packet, if so prefer + // that over the Host information as it will be more specific to our + // process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { @@ -1102,9 +1095,8 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { // architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared - // libraries - // it has, so we really need to take the remote host architecture as our - // defacto architecture in this case. + // libraries it has, so we really need to take the remote host + // architecture as our defacto architecture in this case. if ((process_arch.GetMachine() == llvm::Triple::arm || process_arch.GetMachine() == llvm::Triple::thumb) && @@ -1150,14 +1142,14 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { ? target_arch.GetTriple().getTriple().c_str() : "<null>"); } else { - // The target doesn't have a valid architecture yet, set it from - // the architecture we got from the remote GDB server + // The target doesn't have a valid architecture yet, set it from the + // architecture we got from the remote GDB server GetTarget().SetArchitecture(process_arch); } } - // Find out which StructuredDataPlugins are supported by the - // debug monitor. These plugins transmit data over async $J packets. + // Find out which StructuredDataPlugins are supported by the debug monitor. + // These plugins transmit data over async $J packets. auto supported_packets_array = m_gdb_comm.GetSupportedStructuredDataPlugins(); if (supported_packets_array) @@ -1365,9 +1357,9 @@ Status ProcessGDBRemote::DoResume() { continue_packet_error = true; if (continue_packet_error) { - // Either no vCont support, or we tried to use part of the vCont - // packet that wasn't supported by the remote GDB server. - // We need to try and make a simple packet that can do our continue + // Either no vCont support, or we tried to use part of the vCont packet + // that wasn't supported by the remote GDB server. We need to try and + // make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); @@ -1394,11 +1386,10 @@ Status ProcessGDBRemote::DoResume() { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { - // More that one thread with a signal, yet we don't have - // vCont support and we are being asked to resume each - // thread with a signal, we need to make sure they are - // all the same signal, or we can't issue the continue - // accurately with the current support... + // More that one thread with a signal, yet we don't have vCont + // support and we are being asked to resume each thread with a + // signal, we need to make sure they are all the same signal, or we + // can't issue the continue accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { @@ -1532,7 +1523,6 @@ void ProcessGDBRemote::ClearThreadIDList() { size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) { m_thread_ids.clear(); - m_thread_pcs.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { @@ -1675,9 +1665,8 @@ bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list, } } - // Whatever that is left in old_thread_list_copy are not - // present in new_thread_list. Remove non-existent threads from internal id - // table. + // Whatever that is left in old_thread_list_copy are not present in + // new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i = 0; i < old_num_thread_ids; i++) { ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); @@ -1738,12 +1727,11 @@ bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { return true; // See if we got thread stop info for any threads valid stop info reasons - // threads - // via the "jstopinfo" packet stop reply packet key/value pair? + // threads via the "jstopinfo" packet stop reply packet key/value pair? if (m_jstopinfo_sp) { // If we have "jstopinfo" then we have stop descriptions for all threads - // that have stop reasons, and if there is no entry for a thread, then - // it has no stop reason. + // that have stop reasons, and if there is no entry for a thread, then it + // has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { thread->SetStopInfo(StopInfoSP()); @@ -1771,9 +1759,8 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { - // m_thread_list_real does have its own mutex, but we need to - // hold onto the mutex between the call to - // m_thread_list_real.FindThreadByID(...) + // m_thread_list_real does have its own mutex, but we need to hold onto + // the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us std::lock_guard<std::recursive_mutex> guard( m_thread_list_real.GetMutex()); @@ -1827,10 +1814,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( if (!thread_sp->StopInfoIsUpToDate()) { thread_sp->SetStopInfo(StopInfoSP()); // If there's a memory thread backed by this thread, we need to use it - // to calcualte StopInfo. - ThreadSP memory_thread_sp = - m_thread_list.FindThreadByProtocolID(thread_sp->GetProtocolID()); - if (memory_thread_sp) + // to calculate StopInfo. + if (ThreadSP memory_thread_sp = + m_thread_list.GetBackingThread(thread_sp)) thread_sp = memory_thread_sp; if (exc_type != 0) { @@ -1852,9 +1838,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( ->GetBreakpointSiteList() .FindByAddress(pc); - // If the current pc is a breakpoint site then the StopInfo should - // be set to Breakpoint - // Otherwise, it will be set to Trace. + // If the current pc is a breakpoint site then the StopInfo + // should be set to Breakpoint Otherwise, it will be set to + // Trace. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( @@ -1871,11 +1857,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the - // hit, but if it is for another thread, - // we can just report no reason. We don't need to worry about - // stepping over the breakpoint here, that - // will be taken care of when the thread resumes and notices - // that there's a breakpoint under the pc. + // hit, but if it is for another thread, we can just report no + // reason. We don't need to worry about stepping over the + // breakpoint here, that will be taken care of when the thread + // resumes and notices that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( @@ -1937,13 +1922,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( pc); // If the current pc is a breakpoint site then the StopInfo should - // be set to Breakpoint - // even though the remote stub did not set it as such. This can - // happen when - // the thread is involuntarily interrupted (e.g. due to stops on - // other - // threads) just as it is about to execute the breakpoint - // instruction. + // be set to Breakpoint even though the remote stub did not set it + // as such. This can happen when the thread is involuntarily + // interrupted (e.g. due to stops on other threads) just as it is + // about to execute the breakpoint instruction. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( @@ -1965,11 +1947,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the - // hit, but if it is for another thread, - // we can just report no reason. We don't need to worry about - // stepping over the breakpoint here, that - // will be taken care of when the thread resumes and notices - // that there's a breakpoint under the pc. + // hit, but if it is for another thread, we can just report no + // reason. We don't need to worry about stepping over the + // breakpoint here, that will be taken care of when the thread + // resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread(thread_sp.get())) { if (m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); @@ -1982,8 +1963,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( } } else { // If we were stepping then assume the stop was the result of - // the trace. If we were - // not stepping then report the SIGTRAP. + // the trace. If we were not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step // over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) @@ -2178,15 +2158,15 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { switch (stop_type) { case 'T': case 'S': { - // This is a bit of a hack, but is is required. If we did exec, we - // need to clear our thread lists and also know to rebuild our dynamic - // register info before we lookup and threads and populate the expedited - // register values so we need to know this right away so we can cleanup - // and update our registers. + // This is a bit of a hack, but is is required. If we did exec, we need to + // clear our thread lists and also know to rebuild our dynamic register + // info before we lookup and threads and populate the expedited register + // values so we need to know this right away so we can cleanup and update + // our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { - // Our first stop, make sure we have a process ID, and also make - // sure we know about our registers + // Our first stop, make sure we have a process ID, and also make sure we + // know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) @@ -2232,8 +2212,7 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { m_thread_ids.clear(); // A comma separated list of all threads in the current - // process that includes the thread for this stop reply - // packet + // process that includes the thread for this stop reply packet lldb::tid_t tid; while (!value.empty()) { llvm::StringRef tid_str; @@ -2245,8 +2224,7 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { } else if (key.compare("thread-pcs") == 0) { m_thread_pcs.clear(); // A comma separated list of all threads in the current - // process that includes the thread for this stop reply - // packet + // process that includes the thread for this stop reply packet lldb::addr_t pc; while (!value.empty()) { llvm::StringRef pc_str; @@ -2298,13 +2276,10 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { desc_extractor.GetHexByteString(description); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited - // memory - // that can populate the L1 memory cache in the process so that things - // like - // the frame pointer backchain can be expedited. This will help stack - // backtracing be more efficient by not having to send as many memory - // read - // requests down the remote GDB server. + // memory that can populate the L1 memory cache in the process so that + // things like the frame pointer backchain can be expedited. This will + // help stack backtracing be more efficient by not having to send as + // many memory read requests down the remote GDB server. // Key/value pair format: memory:<addr>=<bytes>; // <addr> is a number whose base will be interpreted by the prefix: @@ -2356,7 +2331,8 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { if (tid == LLDB_INVALID_THREAD_ID) { // A thread id may be invalid if the response is old style 'S' packet // which does not provide the - // thread information. So update the thread list and choose the first one. + // thread information. So update the thread list and choose the first + // one. UpdateThreadIDList(); if (!m_thread_ids.empty()) { @@ -2389,9 +2365,9 @@ void ProcessGDBRemote::RefreshStateAfterStop() { m_thread_ids.clear(); m_thread_pcs.clear(); - // Set the thread stop info. It might have a "threads" key whose value is - // a list of all thread IDs in the current process, so m_thread_ids might - // get set. + // Set the thread stop info. It might have a "threads" key whose value is a + // list of all thread IDs in the current process, so m_thread_ids might get + // set. // Scope for the lock { @@ -2422,8 +2398,8 @@ void ProcessGDBRemote::RefreshStateAfterStop() { m_initial_tid = LLDB_INVALID_THREAD_ID; } - // Let all threads recover from stopping and do any clean up based - // on the previous thread state (if any). + // Let all threads recover from stopping and do any clean up based on the + // previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } @@ -2431,8 +2407,8 @@ Status ProcessGDBRemote::DoHalt(bool &caused_stop) { Status error; if (m_public_state.GetValue() == eStateAttaching) { - // We are being asked to halt during an attach. We need to just close - // our file handle and debugserver will go away, and we can be done... + // We are being asked to halt during an attach. We need to just close our + // file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else caused_stop = m_gdb_comm.Interrupt(); @@ -2475,31 +2451,24 @@ Status ProcessGDBRemote::DoDestroy() { log->Printf("ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the - // process - // they are debugging properly. If the process is sitting at a breakpoint or - // an exception, - // this can cause problems with restarting. So we check to see if any of our - // threads are stopped - // at a breakpoint, and if so we remove all the breakpoints, resume the - // process, and THEN - // destroy it again. + // process they are debugging properly. If the process is sitting at a + // breakpoint or an exception, this can cause problems with restarting. So + // we check to see if any of our threads are stopped at a breakpoint, and if + // so we remove all the breakpoints, resume the process, and THEN destroy it + // again. // // Note, we don't have a good way to test the version of debugserver, but I - // happen to know that - // the set of all the iOS debugservers which don't support - // GetThreadSuffixSupported() and that of - // the debugservers with this bug are equal. There really should be a better - // way to test this! + // happen to know that the set of all the iOS debugservers which don't + // support GetThreadSuffixSupported() and that of the debugservers with this + // bug are equal. There really should be a better way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if - // we resume and then halt and - // get called here to destroy again and we're still at a breakpoint or - // exception, then we should - // just do the straight-forward kill. + // we resume and then halt and get called here to destroy again and we're + // still at a breakpoint or exception, then we should just do the straight- + // forward kill. // // And of course, if we weren't able to stop the process by the time we get - // here, it isn't - // necessary (or helpful) to do any of this. + // here, it isn't necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { @@ -2514,9 +2483,8 @@ Status ProcessGDBRemote::DoDestroy() { "destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled - // Process::Destroy, - // but we really need it to happen here and it doesn't matter if we do - // it twice. + // Process::Destroy, but we really need it to happen here and it + // doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); @@ -2553,12 +2521,11 @@ Status ProcessGDBRemote::DoDestroy() { m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to - // suspend all the threads - // before resuming so they won't get into more trouble. Sadly, for - // the threads stopped with - // the breakpoint or exception, the exception doesn't get cleared if - // it is suspended, so we do - // have to run the risk of letting those threads proceed a bit. + // suspend all the threads before resuming so they won't get into + // more trouble. Sadly, for the threads stopped with the breakpoint + // or exception, the exception doesn't get cleared if it is + // suspended, so we do have to run the risk of letting those threads + // proceed a bit. { std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); @@ -2605,17 +2572,14 @@ Status ProcessGDBRemote::DoDestroy() { if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host - // Platform, then hand the process off - // to debugserver, which becomes the parent process through - // "PT_ATTACH". Then when we go to kill - // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then we - // call waitpid which returns - // with no error and the correct status. But amusingly enough that - // doesn't seem to actually reap + // Platform, then hand the process off to debugserver, which becomes + // the parent process through "PT_ATTACH". Then when we go to kill + // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then + // we call waitpid which returns with no error and the correct + // status. But amusingly enough that doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably - // the kernel is in the process of - // switching ownership back to lldb which was the original parent, and - // gets confused in the handoff. + // the kernel is in the process of switching ownership back to lldb + // which was the original parent, and gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { @@ -2687,9 +2651,8 @@ void ProcessGDBRemote::SetLastStopPacket( if (GetTarget().GetNonStopModeEnabled() == false) m_stop_packet_stack.clear(); - // Add this stop packet to the stop packet stack - // This stack will get popped and examined when we switch to the - // Stopped state + // Add this stop packet to the stop packet stack This stack will get popped + // and examined when we switch to the Stopped state m_stop_packet_stack.push_back(response); } } @@ -2721,12 +2684,11 @@ addr_t ProcessGDBRemote::GetImageInfoAddress() { } void ProcessGDBRemote::WillPublicStop() { - // See if the GDB remote client supports the JSON threads info. - // If so, we gather stop info for all threads, expedited registers, - // expedited memory, runtime queue information (iOS and MacOSX only), - // and more. Expediting memory will help stack backtracing be much - // faster. Expediting registers will make sure we don't have to read - // the thread registers for GPRs. + // See if the GDB remote client supports the JSON threads info. If so, we + // gather stop info for all threads, expedited registers, expedited memory, + // runtime queue information (iOS and MacOSX only), and more. Expediting + // memory will help stack backtracing be much faster. Expediting registers + // will make sure we don't have to read the thread registers for GPRs. m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); if (m_jthreadsinfo_sp) { @@ -2776,14 +2738,13 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has - // already de-quoted any - // 0x7d character escaping that was present in the packet + // already de-quoted any 0x7d character escaping that was present in + // the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us - // too - // much data for some reason. + // too much data for some reason. data_received_size = size; } memcpy(buf, response.GetStringRef().data(), data_received_size); @@ -2807,6 +2768,145 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, return 0; } +Status ProcessGDBRemote::WriteObjectFile( + std::vector<ObjectFile::LoadableData> entries) { + Status error; + // Sort the entries by address because some writes, like those to flash + // memory, must happen in order of increasing address. + std::stable_sort( + std::begin(entries), std::end(entries), + [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) { + return a.Dest < b.Dest; + }); + m_allow_flash_writes = true; + error = Process::WriteObjectFile(entries); + if (error.Success()) + error = FlashDone(); + else + // Even though some of the writing failed, try to send a flash done if some + // of the writing succeeded so the flash state is reset to normal, but + // don't stomp on the error status that was set in the write failure since + // that's the one we want to report back. + FlashDone(); + m_allow_flash_writes = false; + return error; +} + +bool ProcessGDBRemote::HasErased(FlashRange range) { + auto size = m_erased_flash_ranges.GetSize(); + for (size_t i = 0; i < size; ++i) + if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range)) + return true; + return false; +} + +Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) { + Status status; + + MemoryRegionInfo region; + status = GetMemoryRegionInfo(addr, region); + if (!status.Success()) + return status; + + // The gdb spec doesn't say if erasures are allowed across multiple regions, + // but we'll disallow it to be safe and to keep the logic simple by worring + // about only one region's block size. DoMemoryWrite is this function's + // primary user, and it can easily keep writes within a single memory region + if (addr + size > region.GetRange().GetRangeEnd()) { + status.SetErrorString("Unable to erase flash in multiple regions"); + return status; + } + + uint64_t blocksize = region.GetBlocksize(); + if (blocksize == 0) { + status.SetErrorString("Unable to erase flash because blocksize is 0"); + return status; + } + + // Erasures can only be done on block boundary adresses, so round down addr + // and round up size + lldb::addr_t block_start_addr = addr - (addr % blocksize); + size += (addr - block_start_addr); + if ((size % blocksize) != 0) + size += (blocksize - size % blocksize); + + FlashRange range(block_start_addr, size); + + if (HasErased(range)) + return status; + + // We haven't erased the entire range, but we may have erased part of it. + // (e.g., block A is already erased and range starts in A and ends in B). So, + // adjust range if necessary to exclude already erased blocks. + if (!m_erased_flash_ranges.IsEmpty()) { + // Assuming that writes and erasures are done in increasing addr order, + // because that is a requirement of the vFlashWrite command. Therefore, we + // only need to look at the last range in the list for overlap. + const auto &last_range = *m_erased_flash_ranges.Back(); + if (range.GetRangeBase() < last_range.GetRangeEnd()) { + auto overlap = last_range.GetRangeEnd() - range.GetRangeBase(); + // overlap will be less than range.GetByteSize() or else HasErased() + // would have been true + range.SetByteSize(range.GetByteSize() - overlap); + range.SetRangeBase(range.GetRangeBase() + overlap); + } + } + + StreamString packet; + packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(), + (uint64_t)range.GetByteSize()); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) { + m_erased_flash_ranges.Insert(range, true); + } else { + if (response.IsErrorResponse()) + status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64, + addr); + else if (response.IsUnsupportedResponse()) + status.SetErrorStringWithFormat("GDB server does not support flashing"); + else + status.SetErrorStringWithFormat( + "unexpected response to GDB server flash erase packet '%s': '%s'", + packet.GetData(), response.GetStringRef().c_str()); + } + } else { + status.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return status; +} + +Status ProcessGDBRemote::FlashDone() { + Status status; + // If we haven't erased any blocks, then we must not have written anything + // either, so there is no need to actually send a vFlashDone command + if (m_erased_flash_ranges.IsEmpty()) + return status; + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) { + m_erased_flash_ranges.Clear(); + } else { + if (response.IsErrorResponse()) + status.SetErrorStringWithFormat("flash done failed"); + else if (response.IsUnsupportedResponse()) + status.SetErrorStringWithFormat("GDB server does not support flashing"); + else + status.SetErrorStringWithFormat( + "unexpected response to GDB server flash done packet: '%s'", + response.GetStringRef().c_str()); + } + } else { + status.SetErrorStringWithFormat("failed to send flash done packet"); + } + return status; +} + size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { GetMaxMemorySize(); @@ -2819,10 +2919,33 @@ size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size = max_memory_size; } - StreamString packet; - packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); - packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), - endian::InlHostByteOrder()); + StreamGDBRemote packet; + + MemoryRegionInfo region; + Status region_status = GetMemoryRegionInfo(addr, region); + + bool is_flash = + region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes; + + if (is_flash) { + if (!m_allow_flash_writes) { + error.SetErrorString("Writing to flash memory is not allowed"); + return 0; + } + // Keep the write within a flash memory region + if (addr + size > region.GetRange().GetRangeEnd()) + size = region.GetRange().GetRangeEnd() - addr; + // Flash memory must be erased before it can be written + error = FlashErase(addr, size); + if (!error.Success()) + return 0; + packet.Printf("vFlashWrite:%" PRIx64 ":", addr); + packet.PutEscapedBytes(buf, size); + } else { + packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); + packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + } StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, true) == @@ -2918,8 +3041,8 @@ Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { switch (supported) { case eLazyBoolCalculate: - // We should never be deallocating memory without allocating memory - // first so we should never get eLazyBoolCalculate + // We should never be deallocating memory without allocating memory first + // so we should never get eLazyBoolCalculate error.SetErrorString( "tried to deallocate memory without ever allocating memory"); break; @@ -2991,18 +3114,14 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this - // breakpoint type - // is supported by the remote stub. These are set to true by default, and - // later set to false - // only after we receive an unimplemented response when sending a breakpoint - // packet. This means - // initially that unless we were specifically instructed to use a hardware - // breakpoint, LLDB will - // attempt to set a software breakpoint. HardwareRequired() also queries a - // boolean variable which - // indicates if the user specifically asked for hardware breakpoints. If true - // then we will - // skip over software breakpoints. + // breakpoint type is supported by the remote stub. These are set to true by + // default, and later set to false only after we receive an unimplemented + // response when sending a breakpoint packet. This means initially that + // unless we were specifically instructed to use a hardware breakpoint, LLDB + // will attempt to set a software breakpoint. HardwareRequired() also queries + // a boolean variable which indicates if the user specifically asked for + // hardware breakpoints. If true then we will skip over software + // breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) @@ -3015,19 +3134,14 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { return error; } - // SendGDBStoppointTypePacket() will return an error if it was unable to set - // this - // breakpoint. We need to differentiate between a error specific to placing - // this breakpoint - // or if we have learned that this breakpoint type is unsupported. To do - // this, we - // must test the support boolean for this breakpoint type to see if it now - // indicates that - // this breakpoint type is unsupported. If they are still supported then we - // should return + // SendGDBStoppointTypePacket() will return an error if it was unable to + // set this breakpoint. We need to differentiate between a error specific + // to placing this breakpoint or if we have learned that this breakpoint + // type is unsupported. To do this, we must test the support boolean for + // this breakpoint type to see if it now indicates that this breakpoint + // type is unsupported. If they are still supported then we should return // with the error code. If they are now unsupported, then we would like to - // fall through - // and try another form of breakpoint. + // fall through and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( @@ -3038,21 +3152,18 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { } // We reach here when software breakpoints have been found to be - // unsupported. For future - // calls to set a breakpoint, we will not attempt to set a breakpoint with a - // type that is - // known not to be supported. + // unsupported. For future calls to set a breakpoint, we will not attempt + // to set a breakpoint with a type that is known not to be supported. if (log) log->Printf("Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } - // The process of setting a hardware breakpoint is much the same as above. We - // check the - // supported boolean for this breakpoint type, and if it is thought to be - // supported then we - // will try to set this breakpoint with a hardware breakpoint. + // The process of setting a hardware breakpoint is much the same as above. + // We check the supported boolean for this breakpoint type, and if it is + // thought to be supported then we will try to set this breakpoint with a + // hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( @@ -3094,8 +3205,8 @@ Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { return error; } - // As a last resort we want to place a manual breakpoint. An instruction - // is placed into the process memory using memory write packets. + // As a last resort we want to place a manual breakpoint. An instruction is + // placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } @@ -3225,10 +3336,9 @@ Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) { log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); - // See also 'class WatchpointSentry' within StopInfo.cpp. - // This disabling attempt might come from the user-supplied actions, we'll - // route it in order for - // the watchpoint object to intelligently process this action. + // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling + // attempt might come from the user-supplied actions, we'll route it in + // order for the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } @@ -3313,8 +3423,8 @@ Status ProcessGDBRemote::LaunchAndConnectToDebugserver( static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; - // Make debugserver run in its own session so signals generated by - // special terminal key sequences (^C) don't affect debugserver. + // Make debugserver run in its own session so signals generated by special + // terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); const std::weak_ptr<ProcessGDBRemote> this_wp = @@ -3325,27 +3435,22 @@ Status ProcessGDBRemote::LaunchAndConnectToDebugserver( int communication_fd = -1; #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION - // Auto close the sockets we might open up unless everything goes OK. This - // helps us not leak file descriptors when things go wrong. - lldb_utility::CleanUp<int, int> our_socket(-1, -1, close); - lldb_utility::CleanUp<int, int> gdb_socket(-1, -1, close); - // Use a socketpair on non-Windows systems for security and performance // reasons. - { - int sockets[2]; /* the pair of socket descriptors */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { - error.SetErrorToErrno(); - return error; - } - - our_socket.set(sockets[0]); - gdb_socket.set(sockets[1]); + int sockets[2]; /* the pair of socket descriptors */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { + error.SetErrorToErrno(); + return error; } + int our_socket = sockets[0]; + int gdb_socket = sockets[1]; + CleanUp cleanup_our(close, our_socket); + CleanUp cleanup_gdb(close, gdb_socket); + // Don't let any child processes inherit our communication socket - SetCloexecFlag(our_socket.get()); - communication_fd = gdb_socket.get(); + SetCloexecFlag(our_socket); + communication_fd = gdb_socket; #endif error = m_gdb_comm.StartDebugserverProcess( @@ -3359,10 +3464,10 @@ Status ProcessGDBRemote::LaunchAndConnectToDebugserver( if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION - // Our process spawned correctly, we can now set our connection to use our - // end of the socket pair - m_gdb_comm.SetConnection( - new ConnectionFileDescriptor(our_socket.release(), true)); + // Our process spawned correctly, we can now set our connection to use + // our end of the socket pair + cleanup_our.disable(); + m_gdb_comm.SetConnection(new ConnectionFileDescriptor(our_socket, true)); #endif StartAsyncThread(); } @@ -3377,9 +3482,9 @@ Status ProcessGDBRemote::LaunchAndConnectToDebugserver( } if (m_gdb_comm.IsConnected()) { - // Finish the connection process by doing the handshake without connecting - // (send NULL URL) - ConnectToDebugserver(""); + // Finish the connection process by doing the handshake without + // connecting (send NULL URL) + error = ConnectToDebugserver(""); } else { error.SetErrorString("connection failed"); } @@ -3393,8 +3498,8 @@ bool ProcessGDBRemote::MonitorDebugserverProcess( int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { - // "debugserver_pid" argument passed in is the process ID for - // debugserver that we are tracking... + // "debugserver_pid" argument passed in is the process ID for debugserver + // that we are tracking... Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); const bool handled = true; @@ -3410,12 +3515,12 @@ bool ProcessGDBRemote::MonitorDebugserverProcess( if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) return handled; - // Sleep for a half a second to make sure our inferior process has - // time to set its exit status before we set it incorrectly when - // both the debugserver and the inferior process shut down. + // Sleep for a half a second to make sure our inferior process has time to + // set its exit status before we set it incorrectly when both the debugserver + // and the inferior process shut down. usleep(500000); - // If our process hasn't yet exited, debugserver might have died. - // If the process did exit, then we are reaping it. + // If our process hasn't yet exited, debugserver might have died. If the + // process did exit, then we are reaping it. const StateType state = process_sp->GetState(); if (state != eStateInvalid && state != eStateUnloaded && @@ -3438,8 +3543,8 @@ bool ProcessGDBRemote::MonitorDebugserverProcess( process_sp->SetExitStatus(-1, error_str); } - // Debugserver has exited we need to let our ProcessGDBRemote - // know that it no longer has a debugserver instance + // Debugserver has exited we need to let our ProcessGDBRemote know that it no + // longer has a debugserver instance process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; return handled; } @@ -3529,10 +3634,9 @@ bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) { // check for more stop reasons HandleStopReplySequence(); - // if the process is stopped then we need to fake a resume - // so that we can stop properly with the new break. This - // is possible due to SetPrivateState() broadcasting the - // state change as a side effect. + // if the process is stopped then we need to fake a resume so that we can + // stop properly with the new break. This is possible due to + // SetPrivateState() broadcasting the state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } @@ -3605,12 +3709,11 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { response); // We need to immediately clear the thread ID list so we are sure - // to get a valid list of threads. - // The thread ID list might be contained within the "response", or - // the stop reply packet that + // to get a valid list of threads. The thread ID list might be + // contained within the "response", or the stop reply packet that // caused the stop. So clear it now before we give the stop reply - // packet to the process - // using the process->SetLastStopPacket()... + // packet to the process using the + // process->SetLastStopPacket()... process->ClearThreadIDList(); switch (stop_state) { @@ -3647,17 +3750,14 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { // Check to see if we were trying to attach and if we got back // the "E87" error code from debugserver -- this indicates that // the process is not debuggable. Return a slightly more - // helpful - // error message about why the attach failed. + // helpful error message about why the attach failed. if (::strstr(continue_cstr, "vAttach") != NULL && response.GetError() == 0x87) { process->SetExitStatus(-1, "cannot attach to process due to " "System Integrity Protection"); - } - // E01 code from vAttach means that the attach failed - if (::strstr(continue_cstr, "vAttach") != NULL && - response.GetError() == 0x1) { - process->SetExitStatus(-1, "unable to attach"); + } else if (::strstr(continue_cstr, "vAttach") != NULL && + response.GetStatus().Fail()) { + process->SetExitStatus(-1, response.GetStatus().AsCString()); } else { process->SetExitStatus(-1, "lost connection"); } @@ -3769,8 +3869,8 @@ Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG(log, "Check if need to update ignored signals"); - // QPassSignals package is not supported by the server, - // there is no way we can ignore any signals on server side. + // QPassSignals package is not supported by the server, there is no way we + // can ignore any signals on server side. if (!m_gdb_comm.GetQPassSignalsSupported()) return Status(); @@ -3894,9 +3994,9 @@ ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape - // these characters in its packet output -- so we add the quoted version - // of the } character here manually in case we talk to a debugserver which - // un-escapes the characters at packet read time. + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; @@ -3966,9 +4066,9 @@ ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape - // these characters in its packet output -- so we add the quoted version - // of the } character here manually in case we talk to a debugserver which - // un-escapes the characters at packet read time. + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; @@ -3999,9 +4099,9 @@ StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape - // these characters in its packet output -- so we add the quoted version - // of the } character here manually in case we talk to a debugserver which - // un-escapes the characters at packet read time. + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; @@ -4026,14 +4126,14 @@ Status ProcessGDBRemote::ConfigureStructuredData( return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); } -// Establish the largest memory read/write payloads we should use. -// If the remote stub has a max packet size, stay under that size. +// Establish the largest memory read/write payloads we should use. If the +// remote stub has a max packet size, stay under that size. // -// If the remote stub's max packet size is crazy large, use a -// reasonable largeish default. +// If the remote stub's max packet size is crazy large, use a reasonable +// largeish default. // -// If the remote stub doesn't advertise a max packet size, use a -// conservative default. +// If the remote stub doesn't advertise a max packet size, use a conservative +// default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; @@ -4045,15 +4145,15 @@ void ProcessGDBRemote::GetMaxMemorySize() { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; - // Even if the stub says it can support ginormous packets, - // don't exceed our reasonable largeish default packet size. + // Even if the stub says it can support ginormous packets, don't exceed + // our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } - // Memory packet have other overheads too like Maddr,size:#NN - // Instead of calculating the bytes taken by size and addr every - // time, we take a maximum guess here. + // Memory packet have other overheads too like Maddr,size:#NN Instead of + // calculating the bytes taken by size and addr every time, we take a + // maximum guess here. if (stub_max_size > 70) stub_max_size -= 32 + 32 + 6; else { @@ -4140,13 +4240,8 @@ void ProcessGDBRemote::PrefetchModuleSpecs( } } -bool ProcessGDBRemote::GetHostOSVersion(uint32_t &major, uint32_t &minor, - uint32_t &update) { - if (m_gdb_comm.GetOSVersion(major, minor, update)) - return true; - // We failed to get the host OS version, defer to the base - // implementation to correctly invalidate the arguments. - return Process::GetHostOSVersion(major, minor, update); +llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() { + return m_gdb_comm.GetOSVersion(); } namespace { @@ -4237,7 +4332,7 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, } else if (name == "format") { format_set = true; Format format = eFormatInvalid; - if (Args::StringToFormat(value.data(), format, NULL).Success()) + if (OptionArgParser::ToFormat(value.data(), format, NULL).Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; @@ -4311,8 +4406,8 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, } // Only update the register set name if we didn't get a "reg_set" + // attribute. "set_name" will be empty if we didn't have a "reg_set" // attribute. - // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); @@ -4339,8 +4434,8 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, } // namespace {} -// query the target of gdb-remote for extended target information -// return: 'true' on success +// query the target of gdb-remote for extended target information return: +// 'true' on success // 'false' on failure bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { // Make sure LLDB has an XML parser it can use first @@ -4409,15 +4504,27 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { return true; // Keep iterating through all children of the target_node }); + // If the target.xml includes an architecture entry like + // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi) + // <architecture>arm</architecture> (seen from Segger JLink on unspecified arm board) + // use that if we don't have anything better. + if (!arch_to_use.IsValid() && !target_info.arch.empty()) { + if (target_info.arch == "i386:x86-64") + { + // We don't have any information about vendor or OS. + arch_to_use.SetTriple("x86_64--"); + GetTarget().MergeArchitecture(arch_to_use); + } + } + // Initialize these outside of ParseRegisters, since they should not be // reset inside each include feature uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; - // Don't use Process::GetABI, this code gets called from DidAttach, and in - // that context we haven't - // set the Target's architecture yet, so the ABI is also potentially - // incorrect. + // Don't use Process::GetABI, this code gets called from DidAttach, and + // in that context we haven't set the Target's architecture yet, so the + // ABI is also potentially incorrect. ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use); for (auto &feature_node : feature_nodes) { ParseRegisters(feature_node, target_info, this->m_register_info, @@ -4758,8 +4865,8 @@ void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { // do anything Process::ModulesDidLoad(module_list); - // After loading shared libraries, we can ask our remote GDB server if - // it needs any symbols. + // After loading shared libraries, we can ask our remote GDB server if it + // needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } @@ -4817,8 +4924,8 @@ std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( has_used_usec = true; usec_value.getAsInteger(0, curr_used_usec); } else { - // We didn't find what we want, it is probably - // an older version. Bail out. + // We didn't find what we want, it is probably an older version. Bail + // out. profileDataExtractor.SetFilePos(input_file_pos); } } @@ -4840,8 +4947,8 @@ std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( ((real_used_usec > 0) || (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. + // We try to avoid doing too many index id reservation, resulting in + // fast increase of index ids. output_stream << name << ":"; int32_t index_id = AssignIndexIDToThread(thread_id); @@ -4892,7 +4999,7 @@ ParseStructuredDataPacket(llvm::StringRef packet) { if (!packet.consume_front(s_async_json_packet_prefix)) { if (log) { log->Printf( - "GDBRemoteCommmunicationClientBase::%s() received $J packet " + "GDBRemoteCommunicationClientBase::%s() received $J packet " "but was not a StructuredData packet: packet starts with " "%s", __FUNCTION__, @@ -4901,8 +5008,7 @@ ParseStructuredDataPacket(llvm::StringRef packet) { return StructuredData::ObjectSP(); } - // This is an asynchronous JSON packet, destined for a - // StructuredDataPlugin. + // This is an asynchronous JSON packet, destined for a StructuredDataPlugin. StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet); if (log) { if (json_sp) { @@ -5137,8 +5243,9 @@ public: ~CommandObjectProcessGDBRemotePacketMonitor() {} - bool DoExecute(const char *command, CommandReturnObject &result) override { - if (command == NULL || command[0] == '\0') { + bool DoExecute(llvm::StringRef command, + CommandReturnObject &result) override { + if (command.empty()) { result.AppendErrorWithFormat("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); @@ -5150,14 +5257,15 @@ public: if (process) { StreamString packet; packet.PutCString("qRcmd,"); - packet.PutBytesAsRawHex8(command, strlen(command)); + packet.PutBytesAsRawHex8(command.data(), command.size()); bool send_async = true; StringExtractorGDBRemote response; - process->GetGDBRemote().SendPacketAndWaitForResponse( - packet.GetString(), response, send_async); - result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); + process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport( + packet.GetString(), response, send_async, + [&output_strm](llvm::StringRef output) { output_strm << output; }); + result.SetStatus(eReturnStatusSuccessFinishResult); output_strm.Printf(" packet: %s\n", packet.GetData()); const std::string &response_str = response.GetStringRef(); diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 42d1c4ecd6663..45bb2d4c28e71 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -144,6 +144,9 @@ public: size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; + Status + WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override; + size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, Status &error) override; @@ -214,8 +217,7 @@ public: void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) override; - bool GetHostOSVersion(uint32_t &major, uint32_t &minor, - uint32_t &update) override; + llvm::VersionTuple GetHostOSVersion() override; size_t LoadModules(LoadedModuleInfoList &module_list) override; @@ -302,6 +304,11 @@ protected: int64_t m_breakpoint_pc_offset; lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach + bool m_allow_flash_writes; + using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>; + using FlashRange = FlashRangeVector::Entry; + FlashRangeVector m_erased_flash_ranges; + //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- @@ -408,6 +415,12 @@ protected: Status UpdateAutomaticSignalFiltering() override; + Status FlashErase(lldb::addr_t addr, size_t size); + + Status FlashDone(); + + bool HasErased(FlashRange range); + private: //------------------------------------------------------------------ // For ProcessGDBRemote only diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index 27dd03bfc7bc5..a525c16b9f135 100644 --- a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -24,7 +24,7 @@ #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" -#include "Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; @@ -55,7 +55,7 @@ ThreadGDBRemote::~ThreadGDBRemote() { const char *ThreadGDBRemote::GetName() { if (m_thread_name.empty()) - return NULL; + return nullptr; return m_thread_name.c_str(); } @@ -80,10 +80,9 @@ void ThreadGDBRemote::SetQueueInfo(std::string &&queue_name, const char *ThreadGDBRemote::GetQueueName() { // If our cached queue info is valid, then someone called - // ThreadGDBRemote::SetQueueInfo(...) - // with valid information that was gleaned from the stop reply packet. In this - // case we trust - // that the info is valid in m_dispatch_queue_name without refetching it + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it if (CachedQueueInfoIsValid()) { if (m_dispatch_queue_name.empty()) return nullptr; @@ -110,15 +109,14 @@ const char *ThreadGDBRemote::GetQueueName() { return m_dispatch_queue_name.c_str(); } } - return NULL; + return nullptr; } QueueKind ThreadGDBRemote::GetQueueKind() { // If our cached queue info is valid, then someone called - // ThreadGDBRemote::SetQueueInfo(...) - // with valid information that was gleaned from the stop reply packet. In this - // case we trust - // that the info is valid in m_dispatch_queue_name without refetching it + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it if (CachedQueueInfoIsValid()) { return m_queue_kind; } @@ -141,10 +139,9 @@ QueueKind ThreadGDBRemote::GetQueueKind() { queue_id_t ThreadGDBRemote::GetQueueID() { // If our cached queue info is valid, then someone called - // ThreadGDBRemote::SetQueueInfo(...) - // with valid information that was gleaned from the stop reply packet. In this - // case we trust - // that the info is valid in m_dispatch_queue_name without refetching it + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it if (CachedQueueInfoIsValid()) return m_queue_serial_number; @@ -275,11 +272,11 @@ void ThreadGDBRemote::RefreshStateAfterStop() { // Invalidate all registers in our register context. We don't set "force" to // true because the stop reply packet might have had some register values // that were expedited and these will already be copied into the register - // context by the time this function gets called. The GDBRemoteRegisterContext - // class has been made smart enough to detect when it needs to invalidate - // which registers are valid by putting hooks in the register read and - // register supply functions where they check the process stop ID and do - // the right thing. + // context by the time this function gets called. The + // GDBRemoteRegisterContext class has been made smart enough to detect when + // it needs to invalidate which registers are valid by putting hooks in the + // register read and register supply functions where they check the process + // stop ID and do the right thing. const bool force = false; GetRegisterContext()->InvalidateIfNeeded(force); } @@ -292,8 +289,8 @@ void ThreadGDBRemote::Dump(Log *log, uint32_t index) {} bool ThreadGDBRemote::ShouldStop(bool &step_more) { return true; } lldb::RegisterContextSP ThreadGDBRemote::GetRegisterContext() { - if (m_reg_context_sp.get() == NULL) - m_reg_context_sp = CreateRegisterContextForFrame(NULL); + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); return m_reg_context_sp; } @@ -310,7 +307,8 @@ ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) { if (process_sp) { ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); - // read_all_registers_at_once will be true if 'p' packet is not supported. + // read_all_registers_at_once will be true if 'p' packet is not + // supported. bool read_all_registers_at_once = !gdb_process->GetGDBRemote().GetpPacketSupported(GetID()); reg_ctx_sp.reset(new GDBRemoteRegisterContext( @@ -319,7 +317,7 @@ ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) { } } else { Unwind *unwinder = GetUnwinder(); - if (unwinder) + if (unwinder != nullptr) reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame); } return reg_ctx_sp; |