diff options
author | emaste <emaste@FreeBSD.org> | 2013-11-06 16:48:53 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2013-11-06 16:48:53 +0000 |
commit | c727fe695d28799acb499e9961f11ec07d4f9fe2 (patch) | |
tree | 56d79f94966870db1cecd65a7264510a25fd1cba /source/Plugins/Process/gdb-remote | |
parent | 2e8c9206a971efee1b77ad2ae852265d6f4ecaa0 (diff) | |
download | FreeBSD-src-c727fe695d28799acb499e9961f11ec07d4f9fe2.zip FreeBSD-src-c727fe695d28799acb499e9961f11ec07d4f9fe2.tar.gz |
Import lldb as of SVN r194122
Sponsored by: DARPA, AFRL
Diffstat (limited to 'source/Plugins/Process/gdb-remote')
11 files changed, 1374 insertions, 363 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d7efdf2..f67e1b5 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -133,7 +133,11 @@ GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, const char *listener_name, bool is_platform) : Communication(comm_name), +#ifdef LLDB_CONFIGURATION_DEBUG + m_packet_timeout (1000), +#else m_packet_timeout (1), +#endif m_sequence_mutex (Mutex::eMutexTypeRecursive), m_public_is_running (false), m_private_is_running (false), @@ -173,7 +177,7 @@ GDBRemoteCommunication::SendAck () char ch = '+'; const size_t bytes_written = Write (&ch, 1, status, NULL); if (log) - log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + log->Printf ("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); return bytes_written; } @@ -186,7 +190,7 @@ GDBRemoteCommunication::SendNack () char ch = '-'; const size_t bytes_written = Write (&ch, 1, status, NULL); if (log) - log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); return bytes_written; } @@ -222,7 +226,7 @@ GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_le if (!m_history.DidDumpToLog ()) m_history.Dump (log); - log->Printf ("<%4zu> send packet: %.*s", bytes_written, (int)packet.GetSize(), packet.GetData()); + log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)packet.GetSize(), packet.GetData()); } m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written); @@ -456,13 +460,38 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri if (!m_history.DidDumpToLog ()) m_history.Dump (log); - log->Printf ("<%4zu> read packet: %.*s", total_length, (int)(total_length), m_bytes.c_str()); + log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str()); } m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length); - packet_str.assign (m_bytes, content_start, content_length); - + // 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) + packet_str.reserve(m_bytes.length()); + for (std::string::const_iterator c = m_bytes.begin() + content_start; c != m_bytes.begin() + content_start + content_length; ++c) + { + if (*c == '*') + { + // '*' 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. + for (int i = 0; i < repeat_count; ++i) + packet_str.push_back(char_to_repeat); + } + else + { + packet_str.push_back(*c); + } + } + if (m_bytes[0] == '$') { assert (checksum_idx < m_bytes.size()); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index ca594a8..2ac7d20 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -11,6 +11,8 @@ #include "GDBRemoteCommunicationClient.h" // C Includes +#include <sys/stat.h> + // C++ Includes #include <sstream> @@ -20,6 +22,7 @@ #include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" +#include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" @@ -29,10 +32,15 @@ #include "Utility/StringExtractorGDBRemote.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" +#include "lldb/Host/Config.h" using namespace lldb; using namespace lldb_private; +#ifdef LLDB_DISABLE_POSIX +#define SIGSTOP 17 +#endif + //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- @@ -56,6 +64,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply (eLazyBoolCalculate), + m_supports_p (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), m_supports_qfProcessInfo (true), m_supports_qUserName (true), @@ -66,6 +75,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_z2 (true), m_supports_z3 (true), m_supports_z4 (true), + m_supports_QEnvironment (true), + m_supports_QEnvironmentHexEncoded (true), m_curr_tid (LLDB_INVALID_THREAD_ID), m_curr_tid_run (LLDB_INVALID_THREAD_ID), m_num_supported_hardware_watchpoints (0), @@ -79,7 +90,11 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_process_arch(), m_os_version_major (UINT32_MAX), m_os_version_minor (UINT32_MAX), - m_os_version_update (UINT32_MAX) + m_os_version_update (UINT32_MAX), + m_os_build (), + m_os_kernel (), + m_hostname (), + m_default_packet_timeout (0) { } @@ -192,6 +207,7 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings() m_supports_vCont_C = eLazyBoolCalculate; m_supports_vCont_s = eLazyBoolCalculate; m_supports_vCont_S = eLazyBoolCalculate; + m_supports_p = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; m_qProcessInfo_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; @@ -209,6 +225,8 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings() m_supports_z2 = true; m_supports_z3 = true; m_supports_z4 = true; + m_supports_QEnvironment = true; + m_supports_QEnvironmentHexEncoded = true; m_host_arch.Clear(); m_process_arch.Clear(); } @@ -287,6 +305,32 @@ GDBRemoteCommunicationClient::GetVContSupported (char flavor) return false; } +// 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) +{ + if (m_supports_p == eLazyBoolCalculate) + { + StringExtractorGDBRemote response; + m_supports_p = eLazyBoolNo; + char packet[256]; + if (GetThreadSuffixSupported()) + snprintf(packet, sizeof(packet), "p0;thread:%" PRIx64 ";", tid); + else + snprintf(packet, sizeof(packet), "p0"); + + if (SendPacketAndWaitForResponse(packet, response, false)) + { + if (response.IsNormalResponse()) + m_supports_p = eLazyBoolYes; + } + } + return m_supports_p; +} size_t GDBRemoteCommunicationClient::SendPacketAndWaitForResponse @@ -978,15 +1022,61 @@ GDBRemoteCommunicationClient::SendEnvironmentPacket (char const *name_equal_valu if (name_equal_value && name_equal_value[0]) { StreamString packet; - packet.Printf("QEnvironment:%s", name_equal_value); + bool send_hex_encoding = false; + for (const char *p = name_equal_value; *p != '\0' && send_hex_encoding == false; ++p) + { + if (isprint(*p)) + { + switch (*p) + { + case '$': + case '#': + send_hex_encoding = true; + break; + default: + break; + } + } + else + { + // We have non printable characters, lets hex encode this... + send_hex_encoding = true; + } + } + StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + if (send_hex_encoding) { - if (response.IsOKResponse()) - return 0; - uint8_t error = response.GetError(); - if (error) - return error; + if (m_supports_QEnvironmentHexEncoded) + { + packet.PutCString("QEnvironmentHexEncoded:"); + packet.PutBytesAsRawHex8 (name_equal_value, strlen(name_equal_value)); + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironmentHexEncoded = false; + } + } + + } + else if (m_supports_QEnvironment) + { + packet.Printf("QEnvironment:%s", name_equal_value); + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironment = false; + } } } return -1; @@ -1213,6 +1303,15 @@ GDBRemoteCommunicationClient::GetHostInfo (bool force) else --num_keys_decoded; } + else if (name.compare("default_packet_timeout") == 0) + { + m_default_packet_timeout = Args::StringToUInt32(value.c_str(), 0); + if (m_default_packet_timeout > 0) + { + SetPacketTimeout(m_default_packet_timeout); + ++num_keys_decoded; + } + } } @@ -1347,6 +1446,14 @@ GDBRemoteCommunicationClient::GetHostArchitecture () return m_host_arch; } +uint32_t +GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout () +{ + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo (); + return m_default_packet_timeout; +} + addr_t GDBRemoteCommunicationClient::AllocateMemory (size_t size, uint32_t permissions) { @@ -2132,21 +2239,37 @@ GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t } uint16_t -GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort () +GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid) { + pid = LLDB_INVALID_PROCESS_ID; StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qLaunchGDBServer", strlen("qLaunchGDBServer"), response, false)) + StreamString stream; + stream.PutCString("qLaunchGDBServer:port:0;"); + std::string hostname; + if (Host::GetHostname (hostname)) + { + // Make the GDB server we launch only accept connections from this host + stream.Printf("host:%s;", hostname.c_str()); + } + else + { + // Make the GDB server we launch accept connections from any host since we can't figure out the hostname + stream.Printf("host:*;"); + } + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) { std::string name; std::string value; uint16_t port = 0; - //lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { - if (name.size() == 4 && name.compare("port") == 0) + if (name.compare("port") == 0) port = Args::StringToUInt32(value.c_str(), 0, 0); -// if (name.size() == 3 && name.compare("pid") == 0) -// pid = Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); + else if (name.compare("pid") == 0) + pid = Args::StringToUInt64(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); } return port; } @@ -2154,6 +2277,23 @@ GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort () } bool +GDBRemoteCommunicationClient::KillSpawnedProcess (lldb::pid_t pid) +{ + StreamString stream; + stream.Printf ("qKillSpawnedProcess:%" PRId64 , pid); + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + return true; + } + return false; +} + +bool GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid) { if (m_curr_tid == tid) @@ -2222,7 +2362,9 @@ GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtracto assert (packet_len < (int)sizeof(packet)); if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) { - if (response.IsNormalResponse()) + if (response.IsUnsupportedResponse()) + m_supports_qThreadStopInfo = false; + else if (response.IsNormalResponse()) return true; else return false; @@ -2232,8 +2374,6 @@ GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtracto m_supports_qThreadStopInfo = false; } } -// if (SetCurrentThread (tid)) -// return GetStopReply (response); return false; } @@ -2346,3 +2486,326 @@ GDBRemoteCommunicationClient::GetShlibInfoAddr() return LLDB_INVALID_ADDRESS; } +lldb_private::Error +GDBRemoteCommunicationClient::RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_RunCommand:"); + stream.PutBytesAsRawHex8(command, strlen(command)); + stream.PutChar(','); + stream.PutHex32(timeout_sec); + if (working_dir && *working_dir) + { + stream.PutChar(','); + stream.PutBytesAsRawHex8(working_dir, strlen(working_dir)); + } + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return Error("malformed reply"); + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); + if (exitcode == UINT32_MAX) + return Error("unable to run remote process"); + else if (status_ptr) + *status_ptr = exitcode; + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); + if (signo_ptr) + *signo_ptr = signo; + if (response.GetChar() != ',') + return Error("malformed reply"); + std::string output; + response.GetEscapedBinaryData(output); + if (command_output) + command_output->assign(output); + return Error(); + } + return Error("unable to send packet"); +} + +uint32_t +GDBRemoteCommunicationClient::MakeDirectory (const std::string &path, + mode_t mode) +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_IO_MkDir:"); + stream.PutHex32(mode); + stream.PutChar(','); + stream.PutBytesAsRawHex8(path.c_str(), path.size()); + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return response.GetHexMaxU32(false, UINT32_MAX); + } + return UINT32_MAX; + +} + +static uint64_t +ParseHostIOPacketResponse (StringExtractorGDBRemote &response, + uint64_t fail_result, + Error &error) +{ + response.SetFilePos(0); + if (response.GetChar() != 'F') + return fail_result; + int32_t result = response.GetS32 (-2); + if (result == -2) + return fail_result; + if (response.GetChar() == ',') + { + int result_errno = response.GetS32 (-2); + if (result_errno != -2) + error.SetError(result_errno, eErrorTypePOSIX); + else + error.SetError(-1, eErrorTypeGeneric); + } + else + error.Clear(); + return result; +} +lldb::user_id_t +GDBRemoteCommunicationClient::OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:open:"); + std::string path (file_spec.GetPath()); + if (path.empty()) + return UINT64_MAX; + stream.PutCStringAsRawHex8(path.c_str()); + stream.PutChar(','); + const uint32_t posix_open_flags = File::ConvertOpenOptionsForPOSIXOpen(flags); + stream.PutHex32(posix_open_flags); + stream.PutChar(','); + stream.PutHex32(mode); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return ParseHostIOPacketResponse (response, UINT64_MAX, error); + } + return UINT64_MAX; +} + +bool +GDBRemoteCommunicationClient::CloseFile (lldb::user_id_t fd, + Error &error) +{ + lldb_private::StreamString stream; + stream.Printf("vFile:close:%i", (int)fd); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return ParseHostIOPacketResponse (response, -1, error) == 0; + } + return false; +} + +// Extension of host I/O packets to get the file size. +lldb::user_id_t +GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_spec) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:size:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return UINT64_MAX; + uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); + return retcode; + } + return UINT64_MAX; +} + +uint32_t +GDBRemoteCommunicationClient::GetFilePermissions(const lldb_private::FileSpec& file_spec, Error &error) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:mode:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + { + error.SetErrorStringWithFormat ("invalid response to '%s' packet", packet); + return 0; + } + const uint32_t mode = response.GetS32(-1); + if (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.Clear(); + return mode & (S_IRWXU|S_IRWXG|S_IRWXO); + } + else + { + error.SetErrorStringWithFormat ("failed to send '%s' packet", packet); + } + return 0; +} + +uint64_t +GDBRemoteCommunicationClient::ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + Error &error) +{ + lldb_private::StreamString stream; + stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return 0; + uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); + if (retcode == UINT32_MAX) + return retcode; + const char next = (response.Peek() ? *response.Peek() : 0); + if (next == ',') + return 0; + if (next == ';') + { + response.GetChar(); // skip the semicolon + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) + { + const uint64_t data_to_write = std::min<uint64_t>(dst_len, buffer.size()); + if (data_to_write > 0) + memcpy(dst, &buffer[0], data_to_write); + return data_to_write; + } + } + } + return 0; +} + +uint64_t +GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error) +{ + lldb_private::StreamGDBRemote stream; + stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); + stream.PutEscapedBytes(src, src_len); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + { + error.SetErrorStringWithFormat("write file failed"); + return 0; + } + uint64_t bytes_written = response.GetU64(UINT64_MAX); + if (bytes_written == UINT64_MAX) + { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return 0; + } + return bytes_written; + } + else + { + error.SetErrorString ("failed to send vFile:pwrite packet"); + } + return 0; +} + +// Extension of host I/O packets to get whether a file exists. +bool +GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:exists:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + bool retcode = (response.GetChar() != '0'); + return retcode; + } + return false; +} + +bool +GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_spec, + uint64_t &high, + uint64_t &low) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:MD5:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + if (response.Peek() && *response.Peek() == 'x') + return false; + low = response.GetHexMaxU64(false, UINT64_MAX); + high = response.GetHexMaxU64(false, UINT64_MAX); + return true; + } + return false; +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 5bb8387..d5535bb 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -89,7 +89,10 @@ public: GetLaunchSuccess (std::string &error_str); uint16_t - LaunchGDBserverAndGetPort (); + LaunchGDBserverAndGetPort (lldb::pid_t &pid); + + bool + KillSpawnedProcess (lldb::pid_t pid); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'A' packet that delivers program @@ -217,6 +220,9 @@ public: const lldb_private::ArchSpec & GetHostArchitecture (); + + uint32_t + GetHostDefaultPacketTimeout(); const lldb_private::ArchSpec & GetProcessArchitecture (); @@ -225,6 +231,9 @@ public: GetVContSupported (char flavor); bool + GetpPacketSupported (lldb::tid_t tid); + + bool GetVAttachOrWaitSupported (); bool @@ -347,10 +356,61 @@ public: return m_interrupt_sent; } + virtual lldb::user_id_t + OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + lldb_private::Error &error); + + virtual bool + CloseFile (lldb::user_id_t fd, + lldb_private::Error &error); + + virtual lldb::user_id_t + GetFileSize (const lldb_private::FileSpec& file_spec); + + virtual uint32_t + GetFilePermissions(const lldb_private::FileSpec& file_spec, + lldb_private::Error &error); + + virtual uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + lldb_private::Error &error); + + virtual uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + lldb_private::Error &error); + + virtual uint32_t + MakeDirectory (const std::string &path, + mode_t mode); + + virtual bool + GetFileExists (const lldb_private::FileSpec& file_spec); + + virtual lldb_private::Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + + virtual bool + CalculateMD5 (const lldb_private::FileSpec& file_spec, + uint64_t &high, + uint64_t &low); + std::string HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, StringExtractorGDBRemote &inputStringExtractor); - + protected: bool @@ -377,6 +437,7 @@ protected: lldb_private::LazyBool m_watchpoints_trigger_after_instruction; lldb_private::LazyBool m_attach_or_wait_reply; lldb_private::LazyBool m_prepare_for_reg_writing_reply; + lldb_private::LazyBool m_supports_p; bool m_supports_qProcessInfoPID:1, @@ -388,7 +449,9 @@ protected: m_supports_z1:1, m_supports_z2:1, m_supports_z3:1, - m_supports_z4:1; + m_supports_z4:1, + m_supports_QEnvironment:1, + m_supports_QEnvironmentHexEncoded:1; lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations @@ -416,6 +479,7 @@ protected: std::string m_os_build; std::string m_os_kernel; std::string m_hostname; + uint32_t m_default_packet_timeout; bool DecodeProcessInfoResponse (StringExtractorGDBRemote &response, diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 3a14e9f..df036cd 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -7,8 +7,10 @@ // //===----------------------------------------------------------------------===// +#include <errno.h> #include "GDBRemoteCommunicationServer.h" +#include "lldb/Core/StreamGDBRemote.h" // C Includes // C++ Includes @@ -20,6 +22,7 @@ #include "lldb/Core/State.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/Endian.h" +#include "lldb/Host/File.h" #include "lldb/Host/Host.h" #include "lldb/Host/TimeValue.h" #include "lldb/Target/Process.h" @@ -40,11 +43,31 @@ GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : m_async_thread (LLDB_INVALID_HOST_THREAD), m_process_launch_info (), m_process_launch_error (), + m_spawned_pids (), + m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), m_proc_infos (), m_proc_infos_index (0), m_lo_port_num (0), - m_hi_port_num (0) + m_hi_port_num (0), + m_next_port (0), + m_use_port_range (false) { + // We seldom need to override the port number that the debugserver process + // starts with. We just pass in 0 to let the system choose a random port. + // In rare situation where the need arises, use two environment variables + // to override. + uint16_t lo_port_num = 0; + uint16_t hi_port_num = 0; + const char *lo_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_LO_PORT"); + if (lo_port_c_str) + lo_port_num = ::atoi(lo_port_c_str); + const char *hi_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_HI_PORT"); + if (hi_port_c_str) + hi_port_num = ::atoi(hi_port_c_str); + if (lo_port_num && hi_port_num && lo_port_num < hi_port_num) + { + SetPortRange(lo_port_num, hi_port_num); + } } //---------------------------------------------------------------------- @@ -65,7 +88,7 @@ GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() // log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID()); // // StringExtractorGDBRemote packet; -// +// // while () // { // if (packet. @@ -79,9 +102,9 @@ GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() //} // bool -GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, +GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, Error &error, - bool &interrupt, + bool &interrupt, bool &quit) { StringExtractorGDBRemote packet; @@ -103,7 +126,7 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, error.SetErrorString("interrupt received"); interrupt = true; break; - + case StringExtractorGDBRemote::eServerPacketType_unimplemented: return SendUnimplementedResponse (packet.GetStringRef().c_str()) > 0; @@ -112,22 +135,25 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo: return Handle_qfProcessInfo (packet); - + case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo: return Handle_qsProcessInfo (packet); - + case StringExtractorGDBRemote::eServerPacketType_qC: return Handle_qC (packet); - + case StringExtractorGDBRemote::eServerPacketType_qHostInfo: return Handle_qHostInfo (packet); - + case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer: return Handle_qLaunchGDBServer (packet); - + + case StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess: + return Handle_qKillSpawnedProcess (packet); + case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess: return Handle_qLaunchSuccess (packet); - + case StringExtractorGDBRemote::eServerPacketType_qGroupName: return Handle_qGroupName (packet); @@ -142,24 +168,60 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_QEnvironment: return Handle_QEnvironment (packet); - + + case StringExtractorGDBRemote::eServerPacketType_QLaunchArch: + return Handle_QLaunchArch (packet); + case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR: return Handle_QSetDisableASLR (packet); - + case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN: return Handle_QSetSTDIN (packet); - + case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT: return Handle_QSetSTDOUT (packet); - + case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR: return Handle_QSetSTDERR (packet); - + case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir: return Handle_QSetWorkingDir (packet); case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode: return Handle_QStartNoAckMode (packet); + + case StringExtractorGDBRemote::eServerPacketType_qPlatform_IO_MkDir: + return Handle_qPlatform_IO_MkDir (packet); + + case StringExtractorGDBRemote::eServerPacketType_qPlatform_RunCommand: + return Handle_qPlatform_RunCommand (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Open: + return Handle_vFile_Open (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Close: + return Handle_vFile_Close (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_pRead: + return Handle_vFile_pRead (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_pWrite: + return Handle_vFile_pWrite (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Size: + return Handle_vFile_Size (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Mode: + return Handle_vFile_Mode (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Exists: + return Handle_vFile_Exists (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Stat: + return Handle_vFile_Stat (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_MD5: + return Handle_vFile_MD5 (packet); } return true; } @@ -207,7 +269,7 @@ bool GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet) { StreamString response; - + // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 ArchSpec host_arch (Host::GetArchitecture ()); @@ -222,12 +284,12 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet response.Printf ("cputype:%u;", cpu); if (sub != LLDB_INVALID_CPUTYPE) response.Printf ("cpusubtype:%u;", sub); - + if (cpu == ArchSpec::kCore_arm_any) response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes. else response.Printf("watchpoint_exceptions_received:after;"); - + switch (lldb::endian::InlHostByteOrder()) { case eByteOrderBig: response.PutCString ("endian:big;"); break; @@ -235,7 +297,7 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet case eByteOrderPDP: response.PutCString ("endian:pdp;"); break; default: response.PutCString ("endian:unknown;"); break; } - + uint32_t major = UINT32_MAX; uint32_t minor = UINT32_MAX; uint32_t update = UINT32_MAX; @@ -273,7 +335,7 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet response.PutCStringAsRawHex8(s.c_str()); response.PutChar(';'); } - + return SendPacketNoLock (response.GetData(), response.GetSize()) > 0; } @@ -319,7 +381,7 @@ GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote & return SendErrorResponse (1); } -bool +bool GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet) { m_proc_infos_index = 0; @@ -329,7 +391,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa packet.SetFilePos(::strlen ("qfProcessInfo")); if (packet.GetChar() == ':') { - + std::string key; std::string value; while (packet.GetNameColonValue(key, value)) @@ -360,11 +422,11 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa { match_info.SetNameMatchType (eNameMatchContains); } - else if (value.compare("regex") == 0) + else if (value.compare("regex") == 0) { match_info.SetNameMatchType (eNameMatchRegularExpression); } - else + else { success = false; } @@ -405,7 +467,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa { success = false; } - + if (!success) return SendErrorResponse (2); } @@ -420,7 +482,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa return SendErrorResponse (3); } -bool +bool GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet) { if (m_proc_infos_index < m_proc_infos.GetSize()) @@ -433,7 +495,7 @@ GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &pa return SendErrorResponse (4); } -bool +bool GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet) { // Packet format: "qUserName:%i" where %i is the uid @@ -450,10 +512,10 @@ GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet } } return SendErrorResponse (5); - + } -bool +bool GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet) { // Packet format: "qGroupName:%i" where %i is the gid @@ -539,11 +601,11 @@ AcceptPortFromInferior (void *arg) // for (int i=0; i<num_retries; i++) // { // struct proc_bsdinfo bsd_info; -// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, -// (uint64_t) 0, -// &bsd_info, +// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, +// (uint64_t) 0, +// &bsd_info, // PROC_PIDTBSDINFO_SIZE); -// +// // switch (error) // { // case EINVAL: @@ -551,10 +613,10 @@ AcceptPortFromInferior (void *arg) // case ESRCH: // case EPERM: // return false; -// +// // default: // break; -// +// // case 0: // if (bsd_info.pbi_status == SSTOP) // return true; @@ -567,9 +629,9 @@ AcceptPortFromInferior (void *arg) bool GDBRemoteCommunicationServer::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 + // 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... @@ -615,7 +677,7 @@ GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet) if (packet.GetChar() != ',') success = false; } - + if (success) { if (arg_idx == 0) @@ -649,11 +711,11 @@ GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet) response.Printf("QC%" PRIx64, pid); if (m_is_platform) { - // 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 + // 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 + // 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. @@ -666,8 +728,30 @@ GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet) } bool +GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid) +{ + Mutex::Locker locker (m_spawned_pids_mutex); + return m_spawned_pids.erase(pid) > 0; +} +bool +GDBRemoteCommunicationServer::ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton; + server->DebugserverProcessReaped (pid); + return true; +} + +bool GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) { +#ifdef _WIN32 + // No unix sockets on windows + return false; +#else // Spawn a local debugserver as a platform so we can then attach or launch // a process... @@ -677,49 +761,101 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote ConnectionFileDescriptor file_conn; char connect_url[PATH_MAX]; Error error; - char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; - if (::mktemp (unix_socket_name) == NULL) + std::string hostname; + // TODO: /tmp/ should not be hardcoded. User might want to override /tmp + // with the TMPDIR environnement variable + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mkstemp (unix_socket_name) == -1) { - error.SetErrorString ("failed to make temporary path for a unix socket"); + error.SetErrorStringWithFormat("failed to make temporary path for a unix socket: %s", strerror(errno)); } else { + packet.SetFilePos(::strlen ("qLaunchGDBServer:")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = Args::StringToUInt32(value.c_str(), 0, 0); + } + if (port == UINT16_MAX) + port = GetAndUpdateNextPort(); + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); // Spawn a new thread to accept the port that gets bound after // binding to port 0 (zero). - lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name, - AcceptPortFromInferior, - connect_url, - &error); - + lldb::thread_t accept_thread = LLDB_INVALID_HOST_THREAD; + + if (port == 0) + { + accept_thread = Host::ThreadCreate (unix_socket_name, + AcceptPortFromInferior, + connect_url, + &error); + } + if (IS_VALID_LLDB_HOST_THREAD(accept_thread)) { - // Spawn a debugserver and try to get + // Spawn a debugserver and try to get the port it listens to. ProcessLaunchInfo debugserver_launch_info; - error = StartDebugserverProcess ("localhost:0", - unix_socket_name, + StreamString host_and_port; + if (hostname.empty()) + hostname = "localhost"; + host_and_port.Printf("%s:%u", hostname.c_str(), port); + const char *host_and_port_cstr = host_and_port.GetString().c_str(); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Launching debugserver with: %s...\n", host_and_port_cstr); + error = StartDebugserverProcess (host_and_port_cstr, + unix_socket_name, debugserver_launch_info); - + lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + m_spawned_pids.insert(debugserver_pid); + } + Host::StartMonitoringChildProcess (ReapDebugserverProcess, this, debugserver_pid, false); + } + if (error.Success()) { bool success = false; - - thread_result_t accept_thread_result = NULL; - if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) + + if (accept_thread) { - if (accept_thread_result) + thread_result_t accept_thread_result = NULL; + if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) { - uint16_t port = (intptr_t)accept_thread_result; - char response[256]; - const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); - assert (response_len < (int)sizeof(response)); - //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); - success = SendPacketNoLock (response, response_len) > 0; + if (accept_thread_result) + { + port = (intptr_t)accept_thread_result; + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); + assert (response_len < sizeof(response)); + //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); + success = SendPacketNoLock (response, response_len) > 0; + } } } + else + { + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); + assert (response_len < sizeof(response)); + //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); + success = SendPacketNoLock (response, response_len) > 0; + + } ::unlink (unix_socket_name); - + if (!success) { if (debugserver_pid != LLDB_INVALID_PROCESS_ID) @@ -730,7 +866,61 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote } } } - return SendErrorResponse (13); + return SendErrorResponse (9); +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) +{ + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + if (m_is_platform) + { + packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); + + lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return SendErrorResponse (10); + } + Host::Kill (pid, SIGTERM); + + for (size_t i=0; i<10; ++i) + { + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + usleep (10000); + } + + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + Host::Kill (pid, SIGKILL); + + for (size_t i=0; i<10; ++i) + { + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + usleep (10000); + } + } + return SendErrorResponse (10); } bool @@ -738,7 +928,7 @@ GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &p { if (m_process_launch_error.Success()) return SendOKResponse(); - StreamString response; + StreamString response; response.PutChar('E'); response.PutCString(m_process_launch_error.AsCString("<unknown error>")); return SendPacketNoLock (response.GetData(), response.GetSize()); @@ -754,7 +944,22 @@ GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &pa m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); return SendOKResponse (); } - return SendErrorResponse (9); + return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QLaunchArch:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + const char* arch_triple = packet.Peek(); + ArchSpec arch_spec(arch_triple,NULL); + m_process_launch_info.SetArchitecture(arch_spec); + return SendOKResponse(); + } + return SendErrorResponse(12); } bool @@ -792,7 +997,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (10); + return SendErrorResponse (13); } bool @@ -809,7 +1014,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (11); + return SendErrorResponse (14); } bool @@ -826,7 +1031,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (12); + return SendErrorResponse (15); } bool @@ -837,3 +1042,277 @@ GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote & m_send_acks = false; return true; } + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_IO_MkDir:")); + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() != ',') + return false; + std::string path; + packet.GetHexByteString(path); + uint32_t retcode = Host::MakeDirectory(path.c_str(),mode); + StreamString response; + response.PutHex32(retcode); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:open:")); + std::string path; + packet.GetHexByteStringTerminatedBy(path,','); + if (path.size() == 0) + return false; + if (packet.GetChar() != ',') + return false; + uint32_t flags = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() != ',') + return false; + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + Error error; + int fd = ::open (path.c_str(), flags, mode); + const int save_errno = fd == -1 ? errno : 0; + StreamString response; + response.PutChar('F'); + response.Printf("%i", fd); + if (save_errno) + response.Printf(",%i", save_errno); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:close:")); + int fd = packet.GetS32(-1); + Error error; + int err = -1; + int save_errno = 0; + if (fd >= 0) + { + err = close(fd); + save_errno = err == -1 ? errno : 0; + } + else + { + save_errno = EINVAL; + } + StreamString response; + response.PutChar('F'); + response.Printf("%i", err); + if (save_errno) + response.Printf(",%i", save_errno); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + // Not implemented on Windows + return false; +#else + StreamGDBRemote response; + packet.SetFilePos(::strlen("vFile:pread:")); + int fd = packet.GetS32(-1); + if (packet.GetChar() != ',') + return false; + uint64_t count = packet.GetU64(UINT64_MAX); + if (packet.GetChar() != ',') + return false; + uint64_t offset = packet.GetU64(UINT32_MAX); + if (count == UINT64_MAX) + { + response.Printf("F-1:%i", EINVAL); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; + } + std::string buffer(count, 0); + const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset); + const int save_errno = bytes_read == -1 ? errno : 0; + response.PutChar('F'); + response.Printf("%zi", bytes_read); + if (save_errno) + response.Printf(",%i", save_errno); + else + { + response.PutChar(';'); + response.PutEscapedBytes(&buffer[0], bytes_read); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + // Not implemented on Windows + return false; +#else + packet.SetFilePos(::strlen("vFile:pwrite:")); + + StreamGDBRemote response; + response.PutChar('F'); + + int fd = packet.GetU32(UINT32_MAX); + if (packet.GetChar() != ',') + return false; + off_t offset = packet.GetU64(UINT32_MAX); + if (packet.GetChar() != ',') + return false; + std::string buffer; + if (packet.GetEscapedBinaryData(buffer)) + { + const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset); + const int save_errno = bytes_written == -1 ? errno : 0; + response.Printf("%zi", bytes_written); + if (save_errno) + response.Printf(",%i", save_errno); + } + else + { + response.Printf ("-1,%i", EINVAL); + } + + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:size:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + lldb::user_id_t retcode = Host::GetFileSize(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutHex64(retcode); + if (retcode == UINT64_MAX) + { + response.PutChar(','); + response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:mode:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + Error error; + const uint32_t mode = File::GetPermissions(path.c_str(), error); + StreamString response; + response.Printf("F%u", mode); + if (mode == 0 || error.Fail()) + response.Printf(",%i", (int)error.GetError()); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + bool retcode = Host::GetFileExists(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutChar(','); + if (retcode) + response.PutChar('1'); + else + response.PutChar('0'); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_RunCommand:")); + std::string path; + std::string working_dir; + packet.GetHexByteStringTerminatedBy(path,','); + if (path.size() == 0) + return false; + if (packet.GetChar() != ',') + return false; + // FIXME: add timeout to qPlatform_RunCommand 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; + Error err = Host::RunShellCommand(path.c_str(), + working_dir.empty() ? NULL : working_dir.c_str(), + &status, &signo, &output, timeout); + StreamGDBRemote response; + if (err.Fail()) + { + response.PutCString("F,"); + response.PutHex32(UINT32_MAX); + } + else + { + response.PutCString("F,"); + response.PutHex32(status); + response.PutChar(','); + response.PutHex32(signo); + response.PutChar(','); + response.PutEscapedBytes(output.c_str(), output.size()); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet) +{ + return false; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (path.size() == 0) + return false; + uint64_t a,b; + StreamGDBRemote response; + if (Host::CalculateMD5(FileSpec(path.c_str(),false),a,b) == false) + { + response.PutCString("F,"); + response.PutCString("x"); + } + else + { + response.PutCString("F,"); + response.PutHex64(a); + response.PutHex64(b); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index cce0e4e..64f6f8d 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -12,10 +12,12 @@ // C Includes // C++ Includes +#include <vector> +#include <set> // Other libraries and framework includes // Project includes +#include "lldb/Host/Mutex.h" #include "lldb/Target/Process.h" - #include "GDBRemoteCommunication.h" class ProcessGDBRemote; @@ -60,6 +62,21 @@ public: { m_lo_port_num = lo_port_num; m_hi_port_num = hi_port_num; + m_next_port = m_lo_port_num; + m_use_port_range = true; + } + + // If we are using a port range, get and update the next port to be used variable. + // Otherwise, just return 0. + uint16_t + GetAndUpdateNextPort () + { + if (!m_use_port_range) + return 0; + uint16_t val = m_next_port; + if (++m_next_port > m_hi_port_num) + m_next_port = m_lo_port_num; + return val; } protected: @@ -68,11 +85,16 @@ protected: lldb::thread_t m_async_thread; lldb_private::ProcessLaunchInfo m_process_launch_info; lldb_private::Error m_process_launch_error; + std::set<lldb::pid_t> m_spawned_pids; + lldb_private::Mutex m_spawned_pids_mutex; lldb_private::ProcessInstanceInfoList m_proc_infos; uint32_t m_proc_infos_index; uint16_t m_lo_port_num; uint16_t m_hi_port_num; //PortToPIDMap m_port_to_pid_map; + uint16_t m_next_port; + bool m_use_port_range; + size_t SendUnimplementedResponse (const char *packet); @@ -85,7 +107,7 @@ protected: bool Handle_A (StringExtractorGDBRemote &packet); - + bool Handle_qLaunchSuccess (StringExtractorGDBRemote &packet); @@ -94,8 +116,14 @@ protected: bool Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); + + bool + Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); bool + Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet); + + bool Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); bool @@ -120,6 +148,9 @@ protected: Handle_QEnvironment (StringExtractorGDBRemote &packet); bool + Handle_QLaunchArch (StringExtractorGDBRemote &packet); + + bool Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); bool @@ -137,7 +168,47 @@ protected: bool Handle_QSetSTDERR (StringExtractorGDBRemote &packet); + bool + Handle_vFile_Open (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Close (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_pRead (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_pWrite (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Size (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Mode (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Exists (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Stat (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_MD5 (StringExtractorGDBRemote &packet); + + bool + Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet); + private: + bool + DebugserverProcessReaped (lldb::pid_t pid); + + static bool + ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status); + //------------------------------------------------------------------ // For GDBRemoteCommunicationServer only //------------------------------------------------------------------ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp index b1612a5..c4e468f 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -17,6 +17,9 @@ #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" +#ifndef LLDB_DISABLE_PYTHON +#include "lldb/Interpreter/PythonDataObjects.h" +#endif #include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/Utils.h" // Project includes @@ -54,7 +57,7 @@ GDBRemoteRegisterContext::GDBRemoteRegisterContext // Make a heap based buffer that is big enough to store all registers DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0)); m_reg_data.SetData (reg_data_sp); - + m_reg_data.SetByteOrder(thread.GetProcess()->GetByteOrder()); } //---------------------------------------------------------------------- @@ -696,6 +699,7 @@ GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, ui return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num); } + void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h index 3110ddf..7a49d69 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -21,23 +21,20 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Target/RegisterContext.h" +#include "Plugins/Process/Utility/DynamicRegisterInfo.h" + #include "GDBRemoteCommunicationClient.h" class ThreadGDBRemote; class ProcessGDBRemote; class StringExtractor; -class GDBRemoteDynamicRegisterInfo +class GDBRemoteDynamicRegisterInfo : + public DynamicRegisterInfo { public: GDBRemoteDynamicRegisterInfo () : - m_regs (), - m_sets (), - m_set_reg_nums (), - m_reg_names (), - m_reg_alt_names (), - m_set_names (), - m_reg_data_byte_size (0) + DynamicRegisterInfo() { } @@ -46,151 +43,8 @@ public: } void - AddRegister (lldb_private::RegisterInfo reg_info, - lldb_private::ConstString ®_name, - lldb_private::ConstString ®_alt_name, - lldb_private::ConstString &set_name) - { - const uint32_t reg_num = (uint32_t)m_regs.size(); - m_reg_names.push_back (reg_name); - m_reg_alt_names.push_back (reg_alt_name); - reg_info.name = reg_name.AsCString(); - assert (reg_info.name); - reg_info.alt_name = reg_alt_name.AsCString(NULL); - uint32_t i; - if (reg_info.value_regs) - { - for (i=0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i) - m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]); - m_value_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM); - reg_info.value_regs = m_value_regs_map[reg_num].data(); - } - if (reg_info.invalidate_regs) - { - for (i=0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i) - m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]); - m_invalidate_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM); - reg_info.invalidate_regs = m_invalidate_regs_map[reg_num].data(); - } - m_regs.push_back (reg_info); - uint32_t set = GetRegisterSetIndexByName (set_name); - assert (set < m_sets.size()); - assert (set < m_set_reg_nums.size()); - assert (set < m_set_names.size()); - m_set_reg_nums[set].push_back(reg_num); - size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; - if (m_reg_data_byte_size < end_reg_offset) - m_reg_data_byte_size = end_reg_offset; - } - - void - Finalize () - { - for (uint32_t set = 0; set < m_sets.size(); ++set) - { - assert (m_sets.size() == m_set_reg_nums.size()); - m_sets[set].num_registers = m_set_reg_nums[set].size(); - m_sets[set].registers = &m_set_reg_nums[set][0]; - } - } - - size_t - GetNumRegisters() const - { - return m_regs.size(); - } - - size_t - GetNumRegisterSets() const - { - return m_sets.size(); - } - - size_t - GetRegisterDataByteSize() const - { - return m_reg_data_byte_size; - } - - const lldb_private::RegisterInfo * - GetRegisterInfoAtIndex (uint32_t i) const - { - if (i < m_regs.size()) - return &m_regs[i]; - return NULL; - } - - const lldb_private::RegisterSet * - GetRegisterSet (uint32_t i) const - { - if (i < m_sets.size()) - return &m_sets[i]; - return NULL; - } - - uint32_t - GetRegisterSetIndexByName (lldb_private::ConstString &set_name) - { - name_collection::iterator pos, end = m_set_names.end(); - for (pos = m_set_names.begin(); pos != end; ++pos) - { - if (*pos == set_name) - return static_cast<uint32_t>(std::distance (m_set_names.begin(), pos)); - } - - m_set_names.push_back(set_name); - m_set_reg_nums.resize(m_set_reg_nums.size()+1); - lldb_private::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; - m_sets.push_back (new_set); - return static_cast<uint32_t>(m_sets.size() - 1); - } - - uint32_t - ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const - { - reg_collection::const_iterator pos, end = m_regs.end(); - for (pos = m_regs.begin(); pos != end; ++pos) - { - if (pos->kinds[kind] == num) - return static_cast<uint32_t>(std::distance (m_regs.begin(), pos)); - } - - return LLDB_INVALID_REGNUM; - } - void - Clear() - { - m_regs.clear(); - m_sets.clear(); - m_set_reg_nums.clear(); - m_reg_names.clear(); - m_reg_alt_names.clear(); - m_set_names.clear(); - } - - void HardcodeARMRegisters(bool from_scratch); -protected: - //------------------------------------------------------------------ - // Classes that inherit from GDBRemoteRegisterContext can see and modify these - //------------------------------------------------------------------ - typedef std::vector <lldb_private::RegisterInfo> reg_collection; - typedef std::vector <lldb_private::RegisterSet> set_collection; - typedef std::vector <uint32_t> reg_num_collection; - typedef std::vector <reg_num_collection> set_reg_num_collection; - typedef std::vector <lldb_private::ConstString> name_collection; - typedef std::map<uint32_t, reg_num_collection> reg_to_regs_map; - - reg_collection m_regs; - set_collection m_sets; - set_reg_num_collection m_set_reg_nums; - name_collection m_reg_names; - name_collection m_reg_alt_names; - name_collection m_set_names; - reg_to_regs_map m_value_regs_map; - reg_to_regs_map m_invalidate_regs_map; - size_t m_reg_data_byte_size; // The number of bytes required to store all registers }; class GDBRemoteRegisterContext : public lldb_private::RegisterContext diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index d27207f..aff9c7b 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -8,13 +8,16 @@ //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" +#include "lldb/Host/Config.h" // C Includes #include <errno.h> -#include <spawn.h> #include <stdlib.h> +#ifndef LLDB_DISABLE_POSIX +#include <spawn.h> #include <netinet/in.h> #include <sys/mman.h> // for mmap +#endif #include <sys/stat.h> #include <sys/types.h> #include <time.h> @@ -46,6 +49,9 @@ #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" +#ifndef LLDB_DISABLE_PYTHON +#include "lldb/Interpreter/PythonDataObjects.h" +#endif #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" @@ -94,12 +100,14 @@ namespace { g_properties[] = { { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, + { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; enum { - ePropertyPacketTimeout + ePropertyPacketTimeout, + ePropertyTargetDefinitionFile }; class PluginProperties : public Properties @@ -130,6 +138,20 @@ namespace { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); } + + bool + SetPacketTimeout(uint64_t timeout) + { + const uint32_t idx = ePropertyPacketTimeout; + return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, timeout); + } + + FileSpec + GetTargetDefinitionFile () const + { + const uint32_t idx = ePropertyTargetDefinitionFile; + return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); + } }; typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; @@ -254,13 +276,13 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : m_continue_C_tids (), m_continue_s_tids (), m_continue_S_tids (), - m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), m_max_memory_size (512), m_addr_to_mmap_size (), m_thread_create_bp_sp (), m_waiting_for_attach (false), m_destroy_tried_resuming (false), - m_command_sp () + m_command_sp (), + m_breakpoint_pc_offset (0) { m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); @@ -305,6 +327,48 @@ ProcessGDBRemote::GetPluginVersion() return 1; } +bool +ProcessGDBRemote::ParsePythonTargetDefinition(const FileSpec &target_definition_fspec) +{ +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + Error error; + lldb::ScriptInterpreterObjectSP module_object_sp (interpreter->LoadPluginModule(target_definition_fspec, error)); + if (module_object_sp) + { + lldb::ScriptInterpreterObjectSP target_definition_sp (interpreter->GetDynamicSettings(module_object_sp, + &GetTarget(), + "gdb-server-target-definition", + error)); + + PythonDictionary target_dict(target_definition_sp); + + if (target_dict) + { + PythonDictionary host_info_dict (target_dict.GetItemForKey("host-info")); + if (host_info_dict) + { + ArchSpec host_arch (host_info_dict.GetItemForKeyAsString(PythonString("triple"))); + + if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) + { + GetTarget().SetArchitecture(host_arch); + } + + } + m_breakpoint_pc_offset = target_dict.GetItemForKeyAsInteger("breakpoint-pc-offset", 0); + + if (m_register_info.SetRegisterInfo (target_dict, GetTarget().GetArchitecture().GetByteOrder()) > 0) + { + return true; + } + } + } +#endif + return false; +} + + void ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) { @@ -480,6 +544,28 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) } } + // 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 uint32_t host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); + if (host_packet_timeout) + { + GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); + } + + + if (reg_num == 0) + { + FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); + + if (target_definition_fspec) + { + // See if we can get register definitions from a python file + if (ParsePythonTargetDefinition (target_definition_fspec)) + return; + } + } + // 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. @@ -561,6 +647,11 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) GetThreadList(); if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) { + if (!m_target.GetArchitecture().IsValid()) { // Make sure we have an architecture + m_target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); + } + + const StateType state = SetThreadStopInfo (m_last_stop_packet); if (state == eStateStopped) { @@ -759,6 +850,10 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, const ProcessLaunchInfo &launch_ if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) { + if (!m_target.GetArchitecture().IsValid()) { // Make sure we have an architecture + m_target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); + } + SetPrivateState (SetThreadStopInfo (m_last_stop_packet)); if (!disable_stdio) @@ -863,8 +958,6 @@ ProcessGDBRemote::DidLaunchOrAttach () log->Printf ("ProcessGDBRemote::DidLaunch()"); if (GetID() != LLDB_INVALID_PROCESS_ID) { - m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; - BuildDynamicRegisterInfo (false); // See if the GDB server supports the qHostInfo information @@ -1654,7 +1747,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; - addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) @@ -1664,6 +1757,8 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread (thread_sp.get())) { + if(m_breakpoint_pc_offset != 0) + thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else @@ -2220,7 +2315,7 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); - if (bp_site->HardwarePreferred()) + if (bp_site->HardwareRequired()) { // Try and set hardware breakpoint, and if that fails, fall through // and set a software breakpoint? @@ -2230,12 +2325,19 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) { bp_site->SetEnabled(true); bp_site->SetType (BreakpointSite::eHardware); - return error; } + else + { + error.SetErrorString("failed to set hardware breakpoint (hardware breakpoint resources might be exhausted or unavailable)"); + } + } + else + { + error.SetErrorString("hardware breakpoints are not supported"); } + return error; } - - if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointSoftware)) + else if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointSoftware)) { if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) { @@ -2687,7 +2789,7 @@ ProcessGDBRemote::KillDebugserverProcess () { if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { - ::kill (m_debugserver_pid, SIGINT); + Host::Kill (m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } @@ -2794,7 +2896,7 @@ ProcessGDBRemote::StopAsyncThread () } -void * +thread_result_t ProcessGDBRemote::AsyncThread (void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote*) arg; @@ -2916,97 +3018,6 @@ ProcessGDBRemote::AsyncThread (void *arg) return NULL; } -const char * -ProcessGDBRemote::GetDispatchQueueNameForThread -( - addr_t thread_dispatch_qaddr, - std::string &dispatch_queue_name -) -{ - dispatch_queue_name.clear(); - if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) - { - // Cache the dispatch_queue_offsets_addr value so we don't always have - // to look it up - if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) - { - static ConstString g_dispatch_queue_offsets_symbol_name ("dispatch_queue_offsets"); - const Symbol *dispatch_queue_offsets_symbol = NULL; - ModuleSpec libSystem_module_spec (FileSpec("libSystem.B.dylib", false)); - ModuleSP module_sp(GetTarget().GetImages().FindFirstModule (libSystem_module_spec)); - if (module_sp) - dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); - - if (dispatch_queue_offsets_symbol == NULL) - { - ModuleSpec libdispatch_module_spec (FileSpec("libdispatch.dylib", false)); - module_sp = GetTarget().GetImages().FindFirstModule (libdispatch_module_spec); - if (module_sp) - dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); - } - if (dispatch_queue_offsets_symbol) - m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetAddress().GetLoadAddress(&m_target); - - if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) - return NULL; - } - - uint8_t memory_buffer[8]; - DataExtractor data (memory_buffer, - sizeof(memory_buffer), - m_target.GetArchitecture().GetByteOrder(), - m_target.GetArchitecture().GetAddressByteSize()); - - // Excerpt from src/queue_private.h - struct dispatch_queue_offsets_s - { - uint16_t dqo_version; - uint16_t dqo_label; // in version 1-3, offset to string; in version 4+, offset to a pointer to a string - uint16_t dqo_label_size; // in version 1-3, length of string; in version 4+, size of a (void*) in this process - } dispatch_queue_offsets; - - - Error error; - if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) - { - lldb::offset_t data_offset = 0; - if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) - { - if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) - { - data_offset = 0; - lldb::addr_t queue_addr = data.GetAddress(&data_offset); - if (dispatch_queue_offsets.dqo_version >= 4) - { - // libdispatch versions 4+, pointer to dispatch name is in the - // queue structure. - lldb::addr_t pointer_to_label_address = queue_addr + dispatch_queue_offsets.dqo_label; - if (ReadMemory (pointer_to_label_address, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) - { - data_offset = 0; - lldb::addr_t label_addr = data.GetAddress(&data_offset); - ReadCStringFromMemory (label_addr, dispatch_queue_name, error); - } - } - else - { - // libdispatch versions 1-3, dispatch name is a fixed width char array - // in the queue structure. - lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; - dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0'); - size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error); - if (bytes_read < dispatch_queue_offsets.dqo_label_size) - dispatch_queue_name.erase (bytes_read); - } - } - } - } - } - if (dispatch_queue_name.empty()) - return NULL; - return dispatch_queue_name.c_str(); -} - //uint32_t //ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList &matches, std::vector<lldb::pid_t> &pids) //{ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index e104b71..b18ac5b 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -291,6 +291,12 @@ protected: void SetLastStopPacket (const StringExtractorGDBRemote &response); + bool + ParsePythonTargetDefinition(const lldb_private::FileSpec &target_definition_fspec); + + bool + ParseRegisters(lldb_private::ScriptInterpreterObject *registers_array); + //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ @@ -326,13 +332,13 @@ protected: tid_sig_collection m_continue_C_tids; // 'C' for continue with signal tid_collection m_continue_s_tids; // 's' for step tid_sig_collection m_continue_S_tids; // 'S' for step with signal - lldb::addr_t m_dispatch_queue_offsets_addr; size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory MMapMap m_addr_to_mmap_size; lldb::BreakpointSP m_thread_create_bp_sp; bool m_waiting_for_attach; bool m_destroy_tried_resuming; lldb::CommandObjectSP m_command_sp; + int64_t m_breakpoint_pc_offset; bool StartAsyncThread (); @@ -340,7 +346,7 @@ protected: void StopAsyncThread (); - static void * + static lldb::thread_result_t AsyncThread (void *arg); static bool diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index 38fb84d..4e475c8 100644 --- a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -10,16 +10,17 @@ #include "ThreadGDBRemote.h" +#include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataExtractor.h" -#include "lldb/Core/StreamString.h" #include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Unwind.h" -#include "lldb/Breakpoint/Watchpoint.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" @@ -73,13 +74,38 @@ ThreadGDBRemote::GetQueueName () ProcessSP process_sp (GetProcess()); if (process_sp) { - ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); - return gdb_process->GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name); + PlatformSP platform_sp (process_sp->GetTarget().GetPlatform()); + if (platform_sp) + { + m_dispatch_queue_name = platform_sp->GetQueueNameForThreadQAddress (process_sp.get(), m_thread_dispatch_qaddr); + } + if (m_dispatch_queue_name.length() > 0) + { + return m_dispatch_queue_name.c_str(); + } } } return NULL; } +queue_id_t +ThreadGDBRemote::GetQueueID () +{ + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + { + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + PlatformSP platform_sp (process_sp->GetTarget().GetPlatform()); + if (platform_sp) + { + return platform_sp->GetQueueIDForThreadQAddress (process_sp.get(), m_thread_dispatch_qaddr); + } + } + } + return LLDB_INVALID_QUEUE_ID; +} + void ThreadGDBRemote::WillResume (StateType resume_state) { @@ -164,7 +190,6 @@ lldb::RegisterContextSP ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame) { lldb::RegisterContextSP reg_ctx_sp; - const bool read_all_registers_at_once = false; uint32_t concrete_frame_idx = 0; if (frame) @@ -177,6 +202,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. + bool read_all_registers_at_once = !gdb_process->GetGDBRemote().GetpPacketSupported (GetID()); reg_ctx_sp.reset (new GDBRemoteRegisterContext (*this, concrete_frame_idx, gdb_process->m_register_info, read_all_registers_at_once)); } } diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h index 50a3f19..dd4cc03 100644 --- a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -38,6 +38,9 @@ public: virtual const char * GetQueueName (); + virtual lldb::queue_id_t + GetQueueID (); + virtual lldb::RegisterContextSP GetRegisterContext (); |