diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
commit | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch) | |
tree | f42add1021b9f2ac6a69ac7cf6c4499962739a45 /lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp | |
parent | 344a3780b2e33f6ca763666c380202b18aab72a3 (diff) |
Diffstat (limited to 'lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp | 623 |
1 files changed, 356 insertions, 267 deletions
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index b16aed4f5c90..78e722eee080 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -16,7 +16,6 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Host/StringConvert.h" #include "lldb/Host/XML.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/MemoryRegionInfo.h" @@ -65,6 +64,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), + m_supports_vFileSize(true), m_supports_vFileMode(true), + m_supports_vFileExists(true), m_supports_vRun(true), m_host_arch(), m_process_arch(), m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(), m_default_packet_timeout(0), @@ -82,6 +83,8 @@ bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { // 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... + std::chrono::steady_clock::time_point start_of_handshake = + std::chrono::steady_clock::now(); if (SendAck()) { // Wait for any responses that might have been queued up in the remote // GDB server and flush them all @@ -97,8 +100,24 @@ bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { if (QueryNoAckModeSupported()) { return true; } else { - if (error_ptr) - error_ptr->SetErrorString("failed to get reply to handshake packet"); + std::chrono::steady_clock::time_point end_of_handshake = + std::chrono::steady_clock::now(); + auto handshake_timeout = + std::chrono::duration<double>(end_of_handshake - start_of_handshake) + .count(); + if (error_ptr) { + if (packet_result == PacketResult::ErrorDisconnected) + error_ptr->SetErrorString("Connection shut down by remote side " + "while waiting for reply to initial " + "handshake packet"); + else if (packet_result == PacketResult::ErrorReplyTimeout) + error_ptr->SetErrorStringWithFormat( + "failed to get reply to handshake packet within timeout of " + "%.1f seconds", + handshake_timeout); + else + error_ptr->SetErrorString("failed to get reply to handshake packet"); + } } } else { if (error_ptr) @@ -257,12 +276,14 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { m_attach_or_wait_reply = eLazyBoolCalculate; m_avoid_g_packets = eLazyBoolCalculate; m_supports_multiprocess = eLazyBoolCalculate; + m_supports_qSaveCore = eLazyBoolCalculate; m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_qXfer_memory_map_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; + m_uses_native_signals = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; m_supports_qfProcessInfo = true; m_supports_qUserName = true; @@ -312,13 +333,16 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { m_supports_qEcho = eLazyBoolNo; m_supports_QPassSignals = eLazyBoolNo; m_supports_memory_tagging = eLazyBoolNo; + m_supports_qSaveCore = eLazyBoolNo; + m_uses_native_signals = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if // not, we assume no limit // build the qSupported packet std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc", - "multiprocess+"}; + "multiprocess+", "fork-events+", + "vfork-events+"}; StreamString packet; packet.PutCString("qSupported"); for (uint32_t i = 0; i < features.size(); ++i) { @@ -333,10 +357,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { // configuration of the transport before attaching/launching the process. m_qSupported_response = response.GetStringRef().str(); - llvm::SmallVector<llvm::StringRef, 16> server_features; - response.GetStringRef().split(server_features, ';'); - - for (llvm::StringRef x : server_features) { + for (llvm::StringRef x : llvm::split(response.GetStringRef(), ';')) { if (x == "qXfer:auxv:read+") m_supports_qXfer_auxv_read = eLazyBoolYes; else if (x == "qXfer:libraries-svr4:read+") @@ -358,6 +379,10 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() { m_supports_multiprocess = eLazyBoolYes; else if (x == "memory-tagging+") m_supports_memory_tagging = eLazyBoolYes; + else if (x == "qSaveCore+") + m_supports_qSaveCore = eLazyBoolYes; + else if (x == "native-signals+") + m_uses_native_signals = eLazyBoolYes; // Look for a list of compressions in the features list e.g. // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib- // deflate,lzma @@ -500,6 +525,10 @@ LazyBool GDBRemoteCommunicationClient::GetThreadPacketSupported( return eLazyBoolNo; } +bool GDBRemoteCommunicationClient::GetSaveCoreSupported() const { + return m_supports_qSaveCore == eLazyBoolYes; +} + StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { // Get information on all threads at one using the "jThreadsInfo" packet StructuredData::ObjectSP object_sp; @@ -661,54 +690,6 @@ bool GDBRemoteCommunicationClient::GetxPacketSupported() { return m_supports_x; } -GDBRemoteCommunicationClient::PacketResult -GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses( - const char *payload_prefix, std::string &response_string) { - Lock lock(*this); - if (!lock) { - Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | - GDBR_LOG_PACKETS)); - LLDB_LOGF(log, - "error: failed to get packet sequence mutex, not sending " - "packets with prefix '%s'", - payload_prefix); - return PacketResult::ErrorNoSequenceLock; - } - - response_string = ""; - std::string payload_prefix_str(payload_prefix); - unsigned int response_size = 0x1000; - if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet - response_size = GetRemoteMaxPacketSize(); - } - - for (unsigned int offset = 0; true; offset += response_size) { - StringExtractorGDBRemote this_response; - // Construct payload - char sizeDescriptor[128]; - snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, - response_size); - PacketResult result = SendPacketAndWaitForResponseNoLock( - payload_prefix_str + sizeDescriptor, this_response); - if (result != PacketResult::Success) - return result; - - const std::string &this_string = std::string(this_response.GetStringRef()); - - // Check for m or l as first character; l seems to mean this is the last - // chunk - char first_char = *this_string.c_str(); - if (first_char != 'm' && first_char != 'l') { - return PacketResult::ErrorReplyInvalid; - } - // Concatenate the result so far (skipping 'm' or 'l') - response_string.append(this_string, 1, std::string::npos); - if (first_char == 'l') - // We're done - return PacketResult::Success; - } -} - lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) return m_curr_pid; @@ -765,6 +746,11 @@ bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { PacketResult::Success) { if (response.IsOKResponse()) return true; + // GDB does not implement qLaunchSuccess -- but if we used vRun, + // then we already received a successful launch indication via stop + // reason. + if (response.IsUnsupportedResponse() && m_supports_vRun) + return true; if (response.GetChar() == 'E') { // A string the describes what failed when launching... error_str = std::string(response.GetStringRef().substr(1)); @@ -803,6 +789,36 @@ int GDBRemoteCommunicationClient::SendArgumentsPacket( } } if (!argv.empty()) { + // try vRun first + if (m_supports_vRun) { + StreamString packet; + packet.PutCString("vRun"); + for (const char *arg : argv) { + packet.PutChar(';'); + packet.PutBytesAsRawHex8(arg, strlen(arg)); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return -1; + + if (response.IsErrorResponse()) { + uint8_t error = response.GetError(); + if (error) + return error; + return -1; + } + // vRun replies with a stop reason packet + // FIXME: right now we just discard the packet and LLDB queries + // for stop reason again + if (!response.IsUnsupportedResponse()) + return 0; + + m_supports_vRun = false; + } + + // fallback to A StreamString packet; packet.PutChar('A'); for (size_t i = 0, n = argv.size(); i < n; ++i) { @@ -839,7 +855,6 @@ int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) { int GDBRemoteCommunicationClient::SendEnvironmentPacket( char const *name_equal_value) { if (name_equal_value && name_equal_value[0]) { - StreamString packet; bool send_hex_encoding = false; for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding; ++p) { @@ -861,33 +876,43 @@ int GDBRemoteCommunicationClient::SendEnvironmentPacket( } StringExtractorGDBRemote response; - if (send_hex_encoding) { - if (m_supports_QEnvironmentHexEncoded) { - packet.PutCString("QEnvironmentHexEncoded:"); - packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); - if (SendPacketAndWaitForResponse(packet.GetString(), response) == - PacketResult::Success) { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; - if (response.IsUnsupportedResponse()) - m_supports_QEnvironmentHexEncoded = false; - } + // Prefer sending unencoded, if possible and the server supports it. + if (!send_hex_encoding && m_supports_QEnvironment) { + StreamString packet; + packet.Printf("QEnvironment:%s", name_equal_value); + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return -1; + + if (response.IsOKResponse()) + return 0; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironment = false; + else { + uint8_t error = response.GetError(); + if (error) + return error; + return -1; } + } - } else if (m_supports_QEnvironment) { - packet.Printf("QEnvironment:%s", name_equal_value); - if (SendPacketAndWaitForResponse(packet.GetString(), response) == - PacketResult::Success) { - if (response.IsOKResponse()) - return 0; + if (m_supports_QEnvironmentHexEncoded) { + StreamString packet; + packet.PutCString("QEnvironmentHexEncoded:"); + packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return -1; + + if (response.IsOKResponse()) + return 0; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironmentHexEncoded = false; + else { uint8_t error = response.GetError(); if (error) return error; - if (response.IsUnsupportedResponse()) - m_supports_QEnvironment = false; + return -1; } } } @@ -949,26 +974,21 @@ llvm::VersionTuple GDBRemoteCommunicationClient::GetMacCatalystVersion() { return m_maccatalyst_version; } -bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { +llvm::Optional<std::string> GDBRemoteCommunicationClient::GetOSBuildString() { if (GetHostInfo()) { - if (!m_os_build.empty()) { - s = m_os_build; - return true; - } + if (!m_os_build.empty()) + return m_os_build; } - s.clear(); - return false; + return llvm::None; } -bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) { +llvm::Optional<std::string> +GDBRemoteCommunicationClient::GetOSKernelDescription() { if (GetHostInfo()) { - if (!m_os_kernel.empty()) { - s = m_os_kernel; - return true; - } + if (!m_os_kernel.empty()) + return m_os_kernel; } - s.clear(); - return false; + return llvm::None; } bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { @@ -1093,9 +1113,8 @@ void GDBRemoteCommunicationClient::MaybeEnableCompression( if (avail_type != CompressionType::None) { StringExtractorGDBRemote response; - llvm::Twine packet = "QEnableCompression:type:" + avail_name + ";"; - if (SendPacketAndWaitForResponse(packet.str(), response) != - PacketResult::Success) + std::string packet = "QEnableCompression:type:" + avail_name.str() + ";"; + if (SendPacketAndWaitForResponse(packet, response) != PacketResult::Success) return; if (response.IsOKResponse()) { @@ -1360,24 +1379,6 @@ bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { return m_qHostInfo_is_valid == eLazyBoolYes; } -int GDBRemoteCommunicationClient::SendAttach( - lldb::pid_t pid, StringExtractorGDBRemote &response) { - if (pid != LLDB_INVALID_PROCESS_ID) { - char packet[64]; - const int packet_len = - ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid); - UNUSED_IF_ASSERT_DISABLED(packet_len); - assert(packet_len < (int)sizeof(packet)); - if (SendPacketAndWaitForResponse(packet, response) == - PacketResult::Success) { - if (response.IsErrorResponse()) - return response.GetError(); - return 0; - } - } - return -1; -} - int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, size_t data_len) { StreamString packet; @@ -1457,9 +1458,12 @@ bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { return false; } -Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) { +Status GDBRemoteCommunicationClient::Detach(bool keep_stopped, + lldb::pid_t pid) { Status error; + lldb_private::StreamString packet; + packet.PutChar('D'); if (keep_stopped) { if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { char packet[64]; @@ -1481,17 +1485,27 @@ Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) { error.SetErrorString("Stays stopped not supported by this target."); return error; } else { - StringExtractorGDBRemote response; - PacketResult packet_result = SendPacketAndWaitForResponse("D1", response); - if (packet_result != PacketResult::Success) - error.SetErrorString("Sending extended disconnect packet failed."); + packet.PutChar('1'); } - } else { - StringExtractorGDBRemote response; - PacketResult packet_result = SendPacketAndWaitForResponse("D", response); - if (packet_result != PacketResult::Success) - error.SetErrorString("Sending disconnect packet failed."); } + + if (m_supports_multiprocess) { + // Some servers (e.g. qemu) require specifying the PID even if only a single + // process is running. + if (pid == LLDB_INVALID_PROCESS_ID) + pid = GetCurrentProcessID(); + packet.PutChar(';'); + packet.PutHex64(pid); + } else if (pid != LLDB_INVALID_PROCESS_ID) { + error.SetErrorString("Multiprocess extension not supported by the server."); + return error; + } + + StringExtractorGDBRemote response; + PacketResult packet_result = + SendPacketAndWaitForResponse(packet.GetString(), response); + if (packet_result != PacketResult::Success) + error.SetErrorString("Sending isconnect packet failed."); return error; } @@ -1527,17 +1541,17 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( region_info.GetRange().IsValid()) { saw_permissions = true; if (region_info.GetRange().Contains(addr)) { - if (value.find('r') != llvm::StringRef::npos) + if (value.contains('r')) region_info.SetReadable(MemoryRegionInfo::eYes); else region_info.SetReadable(MemoryRegionInfo::eNo); - if (value.find('w') != llvm::StringRef::npos) + if (value.contains('w')) region_info.SetWritable(MemoryRegionInfo::eYes); else region_info.SetWritable(MemoryRegionInfo::eNo); - if (value.find('x') != llvm::StringRef::npos) + if (value.contains('x')) region_info.SetExecutable(MemoryRegionInfo::eYes); else region_info.SetExecutable(MemoryRegionInfo::eNo); @@ -1572,6 +1586,19 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( } } } + } else if (name.equals("type")) { + std::string comma_sep_str = value.str(); + size_t comma_pos; + while ((comma_pos = comma_sep_str.find(',')) != std::string::npos) { + comma_sep_str[comma_pos] = '\0'; + if (comma_sep_str == "stack") { + region_info.SetIsStackMemory(MemoryRegionInfo::eYes); + } + } + // handle final (or only) type of "stack" + if (comma_sep_str == "stack") { + region_info.SetIsStackMemory(MemoryRegionInfo::eYes); + } } else if (name.equals("error")) { StringExtractorGDBRemote error_extractor(value); std::string error_string; @@ -1580,21 +1607,12 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( error.SetErrorString(error_string.c_str()); } else if (name.equals("dirty-pages")) { std::vector<addr_t> dirty_page_list; - std::string comma_sep_str = value.str(); - size_t comma_pos; - addr_t page; - while ((comma_pos = comma_sep_str.find(',')) != std::string::npos) { - comma_sep_str[comma_pos] = '\0'; - page = StringConvert::ToUInt64(comma_sep_str.c_str(), - LLDB_INVALID_ADDRESS, 16); - if (page != LLDB_INVALID_ADDRESS) + for (llvm::StringRef x : llvm::split(value, ',')) { + addr_t page; + x.consume_front("0x"); + if (llvm::to_integer(x, page, 16)) dirty_page_list.push_back(page); - comma_sep_str.erase(0, comma_pos + 1); } - page = StringConvert::ToUInt64(comma_sep_str.c_str(), - LLDB_INVALID_ADDRESS, 16); - if (page != LLDB_INVALID_ADDRESS) - dirty_page_list.push_back(page); region_info.SetDirtyPageList(dirty_page_list); } } @@ -1683,17 +1701,13 @@ Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() { 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; - } + llvm::Expected<std::string> xml = ReadExtFeature("memory-map", ""); + if (!xml) + return Status(xml.takeError()); XMLDocument xml_document; - if (!xml_document.ParseMemory(xml.c_str(), xml.size())) { + if (!xml_document.ParseMemory(xml->c_str(), xml->size())) { error.SetErrorString("Failed to parse memory map xml"); return error; } @@ -2358,24 +2372,6 @@ bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, return false; } -bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) { - // Form non-stop packet request - char packet[32]; - const int packet_len = - ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); - assert(packet_len < (int)sizeof(packet)); - UNUSED_IF_ASSERT_DISABLED(packet_len); - - StringExtractorGDBRemote response; - // Send to target - if (SendPacketAndWaitForResponse(packet, response) == PacketResult::Success) - if (response.IsOKResponse()) - return true; - - // Failed or not supported - return false; -} - static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) { packet.Clear(); @@ -2827,8 +2823,12 @@ GDBRemoteCommunicationClient::GetCurrentProcessAndThreadIDs( if (ch == 'm') { do { auto pid_tid = response.GetPidTid(LLDB_INVALID_PROCESS_ID); + // If we get an invalid response, break out of the loop. + // If there are valid tids, they have been added to ids. + // If there are no valid tids, we'll fall through to the + // bare-iron target handling below. if (!pid_tid) - return {}; + break; ids.push_back(pid_tid.getValue()); ch = response.GetChar(); // Skip the command separator @@ -2959,7 +2959,7 @@ Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, if (response.GetChar() != 'F') return Status("invalid response to '%s' packet", packet.str().c_str()); - return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); + return Status(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); } Status @@ -2980,7 +2980,18 @@ GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, if (response.GetChar() != 'F') return Status("invalid response to '%s' packet", stream.GetData()); - return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); + return Status(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); +} + +static int gdb_errno_to_system(int err) { + switch (err) { +#define HANDLE_ERRNO(name, value) \ + case GDB_##name: \ + return name; +#include "Plugins/Process/gdb-remote/GDBRemoteErrno.def" + default: + return -1; + } } static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, @@ -2988,12 +2999,12 @@ static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, response.SetFilePos(0); if (response.GetChar() != 'F') return fail_result; - int32_t result = response.GetS32(-2); + int32_t result = response.GetS32(-2, 16); if (result == -2) return fail_result; if (response.GetChar() == ',') { - int result_errno = response.GetS32(-2); - if (result_errno != -2) + int result_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (result_errno != -1) error.SetError(result_errno, eErrorTypePOSIX); else error.SetError(-1, eErrorTypeGeneric); @@ -3026,7 +3037,7 @@ GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, Status &error) { lldb_private::StreamString stream; - stream.Printf("vFile:close:%i", (int)fd); + stream.Printf("vFile:close:%x", (int)fd); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response) == PacketResult::Success) { @@ -3035,22 +3046,66 @@ bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, return false; } -// Extension of host I/O packets to get the file size. -lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( - const lldb_private::FileSpec &file_spec) { - std::string path(file_spec.GetPath(false)); +llvm::Optional<GDBRemoteFStatData> +GDBRemoteCommunicationClient::FStat(lldb::user_id_t fd) { lldb_private::StreamString stream; - stream.PutCString("vFile:size:"); - stream.PutStringAsRawHex8(path); + stream.Printf("vFile:fstat:%" PRIx64, fd); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response) == PacketResult::Success) { if (response.GetChar() != 'F') + return llvm::None; + int64_t size = response.GetS64(-1, 16); + if (size > 0 && response.GetChar() == ';') { + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) { + GDBRemoteFStatData out; + if (buffer.size() != sizeof(out)) + return llvm::None; + memcpy(&out, buffer.data(), sizeof(out)); + return out; + } + } + } + return llvm::None; +} + +llvm::Optional<GDBRemoteFStatData> +GDBRemoteCommunicationClient::Stat(const lldb_private::FileSpec &file_spec) { + Status error; + lldb::user_id_t fd = OpenFile(file_spec, File::eOpenOptionReadOnly, 0, error); + if (fd == UINT64_MAX) + return llvm::None; + llvm::Optional<GDBRemoteFStatData> st = FStat(fd); + CloseFile(fd, error); + return st; +} + +// Extension of host I/O packets to get the file size. +lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( + const lldb_private::FileSpec &file_spec) { + if (m_supports_vFileSize) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:size:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) return UINT64_MAX; - uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); - return retcode; + + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') + return UINT64_MAX; + uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); + return retcode; + } + m_supports_vFileSize = false; } - return UINT64_MAX; + + // Fallback to fstat. + llvm::Optional<GDBRemoteFStatData> st = Stat(file_spec); + return st ? st->gdb_st_size : UINT64_MAX; } void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory( @@ -3081,37 +3136,50 @@ void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory( Status GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { - std::string path{file_spec.GetPath(false)}; - Status error; - lldb_private::StreamString stream; - stream.PutCString("vFile:mode:"); - stream.PutStringAsRawHex8(path); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(stream.GetString(), response) == - PacketResult::Success) { - if (response.GetChar() != 'F') { - error.SetErrorStringWithFormat("invalid response to '%s' packet", + if (m_supports_vFileMode) { + std::string path{file_spec.GetPath(false)}; + Status error; + lldb_private::StreamString stream; + stream.PutCString("vFile:mode:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) { + error.SetErrorStringWithFormat("failed to send '%s' packet", stream.GetData()); - } else { - const uint32_t mode = response.GetS32(-1); - if (static_cast<int32_t>(mode) == -1) { - if (response.GetChar() == ',') { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); - else - error.SetErrorToGenericError(); - } else - error.SetErrorToGenericError(); + return error; + } + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') { + error.SetErrorStringWithFormat("invalid response to '%s' packet", + stream.GetData()); } else { - file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); + const uint32_t mode = response.GetS32(-1, 16); + if (static_cast<int32_t>(mode) == -1) { + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + else + error.SetErrorToGenericError(); + } else + error.SetErrorToGenericError(); + } else { + file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } } + return error; + } else { // response.IsUnsupportedResponse() + m_supports_vFileMode = false; } - } else { - error.SetErrorStringWithFormat("failed to send '%s' packet", - stream.GetData()); } - return error; + + // Fallback to fstat. + if (llvm::Optional<GDBRemoteFStatData> st = Stat(file_spec)) { + file_permissions = st->gdb_st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + return Status(); + } + return Status("fstat failed"); } uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, @@ -3119,16 +3187,23 @@ uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, uint64_t dst_len, Status &error) { lldb_private::StreamString stream; - stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, + stream.Printf("vFile:pread:%x,%" PRIx64 ",%" PRIx64, (int)fd, dst_len, offset); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response) == PacketResult::Success) { if (response.GetChar() != 'F') return 0; - uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); - if (retcode == UINT32_MAX) - return retcode; + int64_t retcode = response.GetS64(-1, 16); + if (retcode == -1) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return -1; + } const char next = (response.Peek() ? *response.Peek() : 0); if (next == ',') return 0; @@ -3153,7 +3228,7 @@ uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, uint64_t src_len, Status &error) { lldb_private::StreamGDBRemote stream; - stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); + stream.Printf("vFile:pwrite:%x,%" PRIx64 ",", (int)fd, offset); stream.PutEscapedBytes(src, src_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response) == @@ -3162,15 +3237,15 @@ uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, error.SetErrorStringWithFormat("write file failed"); return 0; } - uint64_t bytes_written = response.GetU64(UINT64_MAX); - if (bytes_written == UINT64_MAX) { + int64_t bytes_written = response.GetS64(-1, 16); + if (bytes_written == -1) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { - int response_errno = response.GetS32(-1); + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } - return 0; + return -1; } return bytes_written; } else { @@ -3194,11 +3269,11 @@ Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, if (SendPacketAndWaitForResponse(stream.GetString(), response) == PacketResult::Success) { if (response.GetChar() == 'F') { - uint32_t result = response.GetU32(UINT32_MAX); + uint32_t result = response.GetHexMaxU32(false, UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { - int response_errno = response.GetS32(-1); + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } @@ -3225,11 +3300,11 @@ Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { if (SendPacketAndWaitForResponse(stream.GetString(), response) == PacketResult::Success) { if (response.GetChar() == 'F') { - uint32_t result = response.GetU32(UINT32_MAX); + uint32_t result = response.GetHexMaxU32(false, UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { - int response_errno = response.GetS32(-1); + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } @@ -3247,21 +3322,33 @@ Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { // Extension of host I/O packets to get whether a file exists. bool GDBRemoteCommunicationClient::GetFileExists( const lldb_private::FileSpec &file_spec) { - std::string path(file_spec.GetPath(false)); - lldb_private::StreamString stream; - stream.PutCString("vFile:exists:"); - stream.PutStringAsRawHex8(path); - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(stream.GetString(), response) == - PacketResult::Success) { - if (response.GetChar() != 'F') - return false; - if (response.GetChar() != ',') + if (m_supports_vFileExists) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:exists:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) return false; - bool retcode = (response.GetChar() != '0'); - return retcode; + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + bool retcode = (response.GetChar() != '0'); + return retcode; + } else + m_supports_vFileExists = false; } - return false; + + // Fallback to open. + Status error; + lldb::user_id_t fd = OpenFile(file_spec, File::eOpenOptionReadOnly, 0, error); + if (fd == UINT64_MAX) + return false; + CloseFile(fd, error); + return true; } bool GDBRemoteCommunicationClient::CalculateMD5( @@ -3790,15 +3877,14 @@ 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 -// 'false' on failure (err set) -bool GDBRemoteCommunicationClient::ReadExtFeature( - const lldb_private::ConstString object, - const lldb_private::ConstString annex, std::string &out, - lldb_private::Status &err) { - - std::stringstream output; +// example: object='features', annex='target.xml' +// return: <xml output> or error +llvm::Expected<std::string> +GDBRemoteCommunicationClient::ReadExtFeature(llvm::StringRef object, + llvm::StringRef annex) { + + std::string output; + llvm::raw_string_ostream output_stream(output); StringExtractorGDBRemote chunk; uint64_t size = GetRemoteMaxPacketSize(); @@ -3812,28 +3898,22 @@ bool GDBRemoteCommunicationClient::ReadExtFeature( while (active) { // send query extended feature packet - std::stringstream packet; - packet << "qXfer:" << object.AsCString("") - << ":read:" << annex.AsCString("") << ":" << std::hex << offset - << "," << std::hex << size; + std::string packet = + ("qXfer:" + object + ":read:" + annex + ":" + + llvm::Twine::utohexstr(offset) + "," + llvm::Twine::utohexstr(size)) + .str(); GDBRemoteCommunication::PacketResult res = - SendPacketAndWaitForResponse(packet.str(), chunk); + SendPacketAndWaitForResponse(packet, chunk); - if (res != GDBRemoteCommunication::PacketResult::Success) { - err.SetErrorString("Error sending $qXfer packet"); - return false; - } - - const std::string &str = std::string(chunk.GetStringRef()); - if (str.length() == 0) { - // should have some data in chunk - err.SetErrorString("Empty response from $qXfer packet"); - return false; + if (res != GDBRemoteCommunication::PacketResult::Success || + chunk.GetStringRef().empty()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error sending $qXfer packet"); } // check packet code - switch (str[0]) { + switch (chunk.GetStringRef()[0]) { // last chunk case ('l'): active = false; @@ -3841,21 +3921,19 @@ bool GDBRemoteCommunicationClient::ReadExtFeature( // more chunks case ('m'): - if (str.length() > 1) - output << &str[1]; - offset += str.length() - 1; + output_stream << chunk.GetStringRef().drop_front(); + offset += chunk.GetStringRef().size() - 1; break; // unknown chunk default: - err.SetErrorString("Invalid continuation code from $qXfer packet"); - return false; + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Invalid continuation code from $qXfer packet"); } } - out = output.str(); - err.Success(); - return true; + return output_stream.str(); } // Notify the target that gdb is prepared to serve symbol lookup requests. @@ -4146,3 +4224,14 @@ void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { GDBRemoteClientBase::OnRunPacketSent(first); m_curr_tid = LLDB_INVALID_THREAD_ID; } + +bool GDBRemoteCommunicationClient::UsesNativeSignals() { + if (m_uses_native_signals == eLazyBoolCalculate) + GetRemoteQSupported(); + if (m_uses_native_signals == eLazyBoolYes) + return true; + + // If the remote didn't indicate native-signal support explicitly, + // check whether it is an old version of lldb-server. + return GetThreadSuffixSupported(); +} |