diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote')
8 files changed, 1020 insertions, 448 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 2ac7d20..2690992 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -65,6 +65,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply (eLazyBoolCalculate), m_supports_p (eLazyBoolCalculate), + m_supports_QSaveRegisterState (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), m_supports_qfProcessInfo (true), m_supports_qUserName (true), @@ -110,17 +111,35 @@ GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() bool GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) { + ResetDiscoverableSettings(); + // Start the read thread after we send the handshake ack since if we // fail to send the handshake ack, there is no reason to continue... if (SendAck()) - return true; - - if (error_ptr) - error_ptr->SetErrorString("failed to send the handshake ack"); + { + // The return value from QueryNoAckModeSupported() is true if the packet + // was sent and _any_ response (including UNIMPLEMENTED) was received), + // or false if no response was received. This quickly tells us if we have + // a live connection to a remote GDB server... + if (QueryNoAckModeSupported()) + { + return true; + } + else + { + if (error_ptr) + error_ptr->SetErrorString("failed to get reply to handshake packet"); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorString("failed to send the handshake ack"); + } return false; } -void +bool GDBRemoteCommunicationClient::QueryNoAckModeSupported () { if (m_supports_not_sending_acks == eLazyBoolCalculate) @@ -136,8 +155,10 @@ GDBRemoteCommunicationClient::QueryNoAckModeSupported () m_send_acks = false; m_supports_not_sending_acks = eLazyBoolYes; } + return true; } } + return false; } void @@ -208,6 +229,7 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings() m_supports_vCont_s = eLazyBoolCalculate; m_supports_vCont_S = eLazyBoolCalculate; m_supports_p = eLazyBoolCalculate; + m_supports_QSaveRegisterState = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; m_qProcessInfo_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; @@ -987,19 +1009,43 @@ GDBRemoteCommunicationClient::GetLaunchSuccess (std::string &error_str) } int -GDBRemoteCommunicationClient::SendArgumentsPacket (char const *argv[]) -{ - if (argv && argv[0]) +GDBRemoteCommunicationClient::SendArgumentsPacket (const ProcessLaunchInfo &launch_info) +{ + // Since we don't get the send argv0 separate from the executable path, we need to + // make sure to use the actual exectuable path found in the launch_info... + std::vector<const char *> argv; + FileSpec exe_file = launch_info.GetExecutableFile(); + std::string exe_path; + const char *arg = NULL; + const Args &launch_args = launch_info.GetArguments(); + if (exe_file) + exe_path = exe_file.GetPath(); + else + { + arg = launch_args.GetArgumentAtIndex(0); + if (arg) + exe_path = arg; + } + if (!exe_path.empty()) + { + argv.push_back(exe_path.c_str()); + for (uint32_t i=1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; ++i) + { + if (arg) + argv.push_back(arg); + } + } + if (!argv.empty()) { StreamString packet; packet.PutChar('A'); - const char *arg; - for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i) + for (size_t i = 0, n = argv.size(); i < n; ++i) { + arg = argv[i]; const int arg_len = strlen(arg); if (i > 0) packet.PutChar(','); - packet.Printf("%i,%i,", arg_len * 2, i); + packet.Printf("%i,%i,", arg_len * 2, (int)i); packet.PutBytesAsRawHex8 (arg, arg_len); } @@ -1783,6 +1829,22 @@ GDBRemoteCommunicationClient::SetSTDERR (char const *path) return -1; } +bool +GDBRemoteCommunicationClient::GetWorkingDir (std::string &cwd) +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse ("qGetWorkingDir", response, false)) + { + if (response.IsUnsupportedResponse()) + return false; + if (response.IsErrorResponse()) + return false; + response.GetHexByteString (cwd); + return !cwd.empty(); + } + return false; +} + int GDBRemoteCommunicationClient::SetWorkingDir (char const *path) { @@ -2244,7 +2306,7 @@ GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid) pid = LLDB_INVALID_PROCESS_ID; StringExtractorGDBRemote response; StreamString stream; - stream.PutCString("qLaunchGDBServer:port:0;"); + stream.PutCString("qLaunchGDBServer;"); std::string hostname; if (Host::GetHostname (hostname)) { @@ -2495,7 +2557,7 @@ GDBRemoteCommunicationClient::RunShellCommand (const char *command, // uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { lldb_private::StreamString stream; - stream.PutCString("qPlatform_RunCommand:"); + stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); stream.PutHex32(timeout_sec); @@ -2534,24 +2596,44 @@ GDBRemoteCommunicationClient::RunShellCommand (const char *command, // return Error("unable to send packet"); } -uint32_t -GDBRemoteCommunicationClient::MakeDirectory (const std::string &path, - mode_t mode) +Error +GDBRemoteCommunicationClient::MakeDirectory (const char *path, + uint32_t file_permissions) { lldb_private::StreamString stream; - stream.PutCString("qPlatform_IO_MkDir:"); - stream.PutHex32(mode); + stream.PutCString("qPlatform_mkdir:"); + stream.PutHex32(file_permissions); stream.PutChar(','); - stream.PutBytesAsRawHex8(path.c_str(), path.size()); + stream.PutBytesAsRawHex8(path, strlen(path)); 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 Error(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); } - return UINT32_MAX; + return Error(); + +} +Error +GDBRemoteCommunicationClient::SetFilePermissions (const char *path, + uint32_t file_permissions) +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_chmod:"); + stream.PutHex32(file_permissions); + stream.PutChar(','); + stream.PutBytesAsRawHex8(path, strlen(path)); + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return Error(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); + } + return Error(); + } static uint64_t @@ -2641,13 +2723,13 @@ GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_sp return UINT64_MAX; } -uint32_t -GDBRemoteCommunicationClient::GetFilePermissions(const lldb_private::FileSpec& file_spec, Error &error) +Error +GDBRemoteCommunicationClient::GetFilePermissions(const char *path, uint32_t &file_permissions) { + Error error; lldb_private::StreamString stream; stream.PutCString("vFile:mode:"); - std::string path (file_spec.GetPath()); - stream.PutCStringAsRawHex8(path.c_str()); + stream.PutCStringAsRawHex8(path); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; @@ -2656,29 +2738,34 @@ GDBRemoteCommunicationClient::GetFilePermissions(const lldb_private::FileSpec& f if (response.GetChar() != 'F') { error.SetErrorStringWithFormat ("invalid response to '%s' packet", packet); - return 0; } - const uint32_t mode = response.GetS32(-1); - if (mode == -1) + else { - if (response.GetChar() == ',') + const uint32_t mode = response.GetS32(-1); + if (mode == -1) { - int response_errno = response.GetS32(-1); - if (response_errno > 0) - error.SetError(response_errno, lldb::eErrorTypePOSIX); + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + else + error.SetErrorToGenericError(); + } else error.SetErrorToGenericError(); } + else + { + file_permissions = mode & (S_IRWXU|S_IRWXG|S_IRWXO); + } } - else - error.Clear(); - return mode & (S_IRWXU|S_IRWXG|S_IRWXO); } else { error.SetErrorStringWithFormat ("failed to send '%s' packet", packet); } - return 0; + return error; } uint64_t @@ -2760,6 +2847,90 @@ GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, return 0; } +Error +GDBRemoteCommunicationClient::CreateSymlink (const char *src, const char *dst) +{ + Error error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:symlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutCStringAsRawHex8(dst); + stream.PutChar(','); + stream.PutCStringAsRawHex8(src); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() == 'F') + { + uint32_t result = response.GetU32(UINT32_MAX); + if (result != 0) + { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } + else + { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("symlink failed"); + } + } + else + { + error.SetErrorString ("failed to send vFile:symlink packet"); + } + return error; +} + +Error +GDBRemoteCommunicationClient::Unlink (const char *path) +{ + Error error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:unlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutCStringAsRawHex8(path); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() == 'F') + { + uint32_t result = response.GetU32(UINT32_MAX); + if (result != 0) + { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } + else + { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("unlink failed"); + } + } + else + { + error.SetErrorString ("failed to send vFile:unlink packet"); + } + return error; +} + // Extension of host I/O packets to get whether a file exists. bool GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) @@ -2809,3 +2980,134 @@ GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_s } return false; } + +bool +GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, StringExtractorGDBRemote &response) +{ + Mutex::Locker locker; + if (GetSequenceMutex (locker, "Didn't get sequence mutex for p packet.")) + { + const bool thread_suffix_supported = GetThreadSuffixSupported(); + + if (thread_suffix_supported || SetCurrentThread(tid)) + { + char packet[64]; + int packet_len = 0; + if (thread_suffix_supported) + packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, tid); + else + packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg); + assert (packet_len < ((int)sizeof(packet) - 1)); + return SendPacketAndWaitForResponse(packet, response, false); + } + } + return false; + +} + + +bool +GDBRemoteCommunicationClient::ReadAllRegisters (lldb::tid_t tid, StringExtractorGDBRemote &response) +{ + Mutex::Locker locker; + if (GetSequenceMutex (locker, "Didn't get sequence mutex for g packet.")) + { + const bool thread_suffix_supported = GetThreadSuffixSupported(); + + if (thread_suffix_supported || SetCurrentThread(tid)) + { + char packet[64]; + int packet_len = 0; + // Get all registers in one packet + if (thread_suffix_supported) + packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", tid); + else + packet_len = ::snprintf (packet, sizeof(packet), "g"); + assert (packet_len < ((int)sizeof(packet) - 1)); + return SendPacketAndWaitForResponse(packet, response, false); + } + } + return false; +} +bool +GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save_id) +{ + save_id = 0; // Set to invalid save ID + if (m_supports_QSaveRegisterState == eLazyBoolNo) + return false; + + m_supports_QSaveRegisterState = eLazyBoolYes; + Mutex::Locker locker; + if (GetSequenceMutex (locker, "Didn't get sequence mutex for QSaveRegisterState.")) + { + const bool thread_suffix_supported = GetThreadSuffixSupported(); + if (thread_suffix_supported || SetCurrentThread(tid)) + { + char packet[256]; + if (thread_suffix_supported) + ::snprintf (packet, sizeof(packet), "QSaveRegisterState;thread:%4.4" PRIx64 ";", tid); + else + ::strncpy (packet, "QSaveRegisterState", sizeof(packet)); + + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response, false)) + { + if (response.IsUnsupportedResponse()) + { + // This packet isn't supported, don't try calling it again + m_supports_QSaveRegisterState = eLazyBoolNo; + } + + const uint32_t response_save_id = response.GetU32(0); + if (response_save_id != 0) + { + save_id = response_save_id; + return true; + } + } + } + } + return false; +} + +bool +GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t save_id) +{ + // We use the "m_supports_QSaveRegisterState" variable here becuase the + // QSaveRegisterState and QRestoreRegisterState packets must both be supported in + // order to be useful + if (m_supports_QSaveRegisterState == eLazyBoolNo) + return false; + + Mutex::Locker locker; + if (GetSequenceMutex (locker, "Didn't get sequence mutex for QRestoreRegisterState.")) + { + const bool thread_suffix_supported = GetThreadSuffixSupported(); + if (thread_suffix_supported || SetCurrentThread(tid)) + { + char packet[256]; + if (thread_suffix_supported) + ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u;thread:%4.4" PRIx64 ";", save_id, tid); + else + ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u" PRIx64 ";", save_id); + + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response, false)) + { + if (response.IsOKResponse()) + { + return true; + } + else if (response.IsUnsupportedResponse()) + { + // This packet isn't supported, don't try calling this packet or + // QSaveRegisterState again... + m_supports_QSaveRegisterState = eLazyBoolNo; + } + } + } + } + return false; +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index d5535bb..564afbb 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -38,7 +38,6 @@ public: //------------------------------------------------------------------ GDBRemoteCommunicationClient(bool is_platform); - virtual ~GDBRemoteCommunicationClient(); //------------------------------------------------------------------ @@ -65,10 +64,16 @@ public: size_t packet_length, StringExtractorGDBRemote &response); - virtual bool + bool GetThreadSuffixSupported (); - void + // This packet is usually sent first and the boolean return value + // indicates if the packet was send and any response was received + // even in the response is UNIMPLEMENTED. If the packet failed to + // get a response, then false is returned. This quickly tells us + // if we were able to connect and communicte with the remote GDB + // server + bool QueryNoAckModeSupported (); void @@ -109,7 +114,7 @@ public: /// response was received. //------------------------------------------------------------------ int - SendArgumentsPacket (char const *argv[]); + SendArgumentsPacket (const lldb_private::ProcessLaunchInfo &launch_info); //------------------------------------------------------------------ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the @@ -185,7 +190,10 @@ public: //------------------------------------------------------------------ /// Sets the working directory to \a path for a process that will - /// be launched with the 'A' packet. + /// be launched with the 'A' packet for non platform based + /// connections. If this packet is sent to a GDB server that + /// implements the platform, it will change the current working + /// directory for the platform process. /// /// @param[in] path /// The path to a directory to use when launching our processs @@ -196,6 +204,19 @@ public: int SetWorkingDir (char const *path); + //------------------------------------------------------------------ + /// Gets the current working directory of a remote platform GDB + /// server. + /// + /// @param[out] cwd + /// The current working directory on the remote platform. + /// + /// @return + /// Boolean for success + //------------------------------------------------------------------ + bool + GetWorkingDir (std::string &cwd); + lldb::addr_t AllocateMemory (size_t size, uint32_t permissions); @@ -356,45 +377,54 @@ public: return m_interrupt_sent; } - virtual lldb::user_id_t + lldb::user_id_t OpenFile (const lldb_private::FileSpec& file_spec, uint32_t flags, mode_t mode, lldb_private::Error &error); - virtual bool + bool CloseFile (lldb::user_id_t fd, lldb_private::Error &error); - virtual lldb::user_id_t + 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); + lldb_private::Error + GetFilePermissions(const char *path, uint32_t &file_permissions); + + lldb_private::Error + SetFilePermissions(const char *path, uint32_t file_permissions); - virtual uint64_t + uint64_t ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, lldb_private::Error &error); - virtual uint64_t + 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); + lldb_private::Error + CreateSymlink (const char *src, + const char *dst); - virtual bool + lldb_private::Error + Unlink (const char *path); + + lldb_private::Error + MakeDirectory (const char *path, + uint32_t mode); + + bool GetFileExists (const lldb_private::FileSpec& file_spec); - virtual lldb_private::Error + 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 @@ -402,7 +432,7 @@ public: 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 + bool CalculateMD5 (const lldb_private::FileSpec& file_spec, uint64_t &high, uint64_t &low); @@ -411,6 +441,21 @@ public: HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, StringExtractorGDBRemote &inputStringExtractor); + bool + ReadRegister(lldb::tid_t tid, + uint32_t reg_num, + StringExtractorGDBRemote &response); + + bool + ReadAllRegisters (lldb::tid_t tid, + StringExtractorGDBRemote &response); + + bool + SaveRegisterState (lldb::tid_t tid, uint32_t &save_id); + + bool + RestoreRegisterState (lldb::tid_t tid, uint32_t save_id); + protected: bool @@ -438,6 +483,7 @@ protected: lldb_private::LazyBool m_attach_or_wait_reply; lldb_private::LazyBool m_prepare_for_reg_writing_reply; lldb_private::LazyBool m_supports_p; + lldb_private::LazyBool m_supports_QSaveRegisterState; bool m_supports_qProcessInfoPID:1, diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index df036cd..7cc3a05 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -47,27 +47,9 @@ GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : 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_next_port (0), - m_use_port_range (false) + m_port_map (), + m_port_offset(0) { - // 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); - } } //---------------------------------------------------------------------- @@ -166,6 +148,9 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_qUserName: return Handle_qUserName (packet); + case StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir: + return Handle_qGetWorkingDir(packet); + case StringExtractorGDBRemote::eServerPacketType_QEnvironment: return Handle_QEnvironment (packet); @@ -190,38 +175,47 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode: return Handle_QStartNoAckMode (packet); - case StringExtractorGDBRemote::eServerPacketType_qPlatform_IO_MkDir: - return Handle_qPlatform_IO_MkDir (packet); + case StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir: + return Handle_qPlatform_mkdir (packet); - case StringExtractorGDBRemote::eServerPacketType_qPlatform_RunCommand: - return Handle_qPlatform_RunCommand (packet); + case StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod: + return Handle_qPlatform_chmod (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Open: + case StringExtractorGDBRemote::eServerPacketType_qPlatform_shell: + return Handle_qPlatform_shell (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_open: return Handle_vFile_Open (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Close: + case StringExtractorGDBRemote::eServerPacketType_vFile_close: return Handle_vFile_Close (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_pRead: + case StringExtractorGDBRemote::eServerPacketType_vFile_pread: return Handle_vFile_pRead (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_pWrite: + case StringExtractorGDBRemote::eServerPacketType_vFile_pwrite: return Handle_vFile_pWrite (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Size: + case StringExtractorGDBRemote::eServerPacketType_vFile_size: return Handle_vFile_Size (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Mode: + case StringExtractorGDBRemote::eServerPacketType_vFile_mode: return Handle_vFile_Mode (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Exists: + case StringExtractorGDBRemote::eServerPacketType_vFile_exists: return Handle_vFile_Exists (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_Stat: + case StringExtractorGDBRemote::eServerPacketType_vFile_stat: return Handle_vFile_Stat (packet); - case StringExtractorGDBRemote::eServerPacketType_vFile_MD5: + case StringExtractorGDBRemote::eServerPacketType_vFile_md5: return Handle_vFile_MD5 (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_symlink: + return Handle_vFile_symlink (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_unlink: + return Handle_vFile_unlink (packet); } return true; } @@ -329,12 +323,33 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet response.PutCStringAsRawHex8(s.c_str()); response.PutChar(';'); } +#if defined(__APPLE__) + +#if defined(__arm__) + // For iOS devices, we are connected through a USB Mux so we never pretend + // to actually have a hostname as far as the remote lldb that is connecting + // to this lldb-platform is concerned + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8("localhost"); + response.PutChar(';'); +#else // #if defined(__arm__) + if (Host::GetHostname (s)) + { + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + +#endif // #if defined(__arm__) + +#else // #if defined(__APPLE__) if (Host::GetHostname (s)) { response.PutCString ("hostname:"); response.PutCStringAsRawHex8(s.c_str()); response.PutChar(';'); } +#endif // #if defined(__APPLE__) return SendPacketNoLock (response.GetData(), response.GetSize()) > 0; } @@ -731,6 +746,7 @@ bool GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid) { Mutex::Locker locker (m_spawned_pids_mutex); + FreePortForProcess(pid); return m_spawned_pids.erase(pid) > 0; } bool @@ -764,105 +780,119 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote 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) + packet.SetFilePos(::strlen ("qLaunchGDBServer;")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) { - error.SetErrorStringWithFormat("failed to make temporary path for a unix socket: %s", strerror(errno)); + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = Args::StringToUInt32(value.c_str(), 0, 0); } - 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(); + if (port == UINT16_MAX) + port = GetNextAvailablePort(); - ::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 = LLDB_INVALID_HOST_THREAD; + // Spawn a new thread to accept the port that gets bound after + // binding to port 0 (zero). + lldb::thread_t accept_thread = LLDB_INVALID_HOST_THREAD; + const char *unix_socket_name = NULL; + char unix_socket_name_buf[PATH_MAX] = "/tmp/XXXXXXXXX"; - if (port == 0) + if (port == 0) + { + if (::mkstemp (unix_socket_name_buf) == 0) { + unix_socket_name = unix_socket_name_buf; + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); accept_thread = Host::ThreadCreate (unix_socket_name, AcceptPortFromInferior, connect_url, &error); } + else + { + error.SetErrorStringWithFormat("failed to make temporary path for a unix socket: %s", strerror(errno)); + } + } - if (IS_VALID_LLDB_HOST_THREAD(accept_thread)) + if (error.Success()) + { + // Spawn a debugserver and try to get the port it listens to. + ProcessLaunchInfo debugserver_launch_info; + 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); + + debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); + + 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) { - // Spawn a debugserver and try to get the port it listens to. - ProcessLaunchInfo debugserver_launch_info; - 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); - } + Mutex::Locker locker (m_spawned_pids_mutex); + m_spawned_pids.insert(debugserver_pid); + if (port > 0) + AssociatePortWithProcess(port, debugserver_pid); + } + else + { + if (port > 0) + FreePort (port); + } - if (error.Success()) - { - bool success = false; + if (error.Success()) + { + bool success = false; - if (accept_thread) + if (IS_VALID_LLDB_HOST_THREAD(accept_thread)) + { + thread_result_t accept_thread_result = NULL; + if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) { - thread_result_t accept_thread_result = NULL; - if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) + if (accept_thread_result) { - 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; - } + port = (intptr_t)accept_thread_result; + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); + 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; + } + else + { + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); + 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); + } + Host::Unlink (unix_socket_name); - if (!success) - { - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) - ::kill (debugserver_pid, SIGINT); - } - return success; + if (!success) + { + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + ::kill (debugserver_pid, SIGINT); } + return success; + } + else if (accept_thread) + { + Host::Unlink (unix_socket_name); } } } @@ -896,7 +926,7 @@ GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemo { Mutex::Locker locker (m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return true; + return SendOKResponse(); } usleep (10000); } @@ -905,7 +935,7 @@ GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemo { Mutex::Locker locker (m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return true; + return SendOKResponse(); } Host::Kill (pid, SIGKILL); @@ -915,12 +945,12 @@ GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemo { Mutex::Locker locker (m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return true; + return SendOKResponse(); } usleep (10000); } } - return SendErrorResponse (10); + return SendErrorResponse (11); } bool @@ -944,7 +974,7 @@ GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &pa m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); return SendOKResponse (); } - return SendErrorResponse (11); + return SendErrorResponse (12); } bool @@ -959,7 +989,7 @@ GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &pack m_process_launch_info.SetArchitecture(arch_spec); return SendOKResponse(); } - return SendErrorResponse(12); + return SendErrorResponse(13); } bool @@ -979,11 +1009,61 @@ GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &p packet.SetFilePos(::strlen ("QSetWorkingDir:")); std::string path; packet.GetHexByteString(path); - m_process_launch_info.SwapWorkingDirectory (path); + if (m_is_platform) + { +#ifdef _WIN32 + // Not implemented on Windows + return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_QSetWorkingDir unimplemented"); +#else + // If this packet is sent to a platform, then change the current working directory + if (::chdir(path.c_str()) != 0) + return SendErrorResponse(errno); +#endif + } + else + { + m_process_launch_info.SwapWorkingDirectory (path); + } return SendOKResponse (); } bool +GDBRemoteCommunicationServer::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet) +{ + StreamString response; + + if (m_is_platform) + { + // If this packet is sent to a platform, then change the current working directory + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) == NULL) + { + return SendErrorResponse(errno); + } + else + { + response.PutBytesAsRawHex8(cwd, strlen(cwd)); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; + } + } + else + { + const char *working_dir = m_process_launch_info.GetWorkingDirectory(); + if (working_dir && working_dir[0]) + { + response.PutBytesAsRawHex8(working_dir, strlen(working_dir)); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; + } + else + { + return SendErrorResponse(14); + } + } +} + +bool GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen ("QSetSTDIN:")); @@ -997,7 +1077,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (13); + return SendErrorResponse (15); } bool @@ -1014,7 +1094,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (14); + return SendErrorResponse (16); } bool @@ -1031,7 +1111,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (15); + return SendErrorResponse (17); } bool @@ -1044,19 +1124,40 @@ GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote & } bool -GDBRemoteCommunicationServer::Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet) +GDBRemoteCommunicationServer::Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet) { - packet.SetFilePos(::strlen("qPlatform_IO_MkDir:")); + packet.SetFilePos(::strlen("qPlatform_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; + if (packet.GetChar() == ',') + { + std::string path; + packet.GetHexByteString(path); + Error error = Host::MakeDirectory(path.c_str(),mode); + if (error.Success()) + return SendPacketNoLock ("OK", 2); + else + return SendErrorResponse(error.GetError()); + } + return SendErrorResponse(20); +} + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_chmod (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_chmod:")); + + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() == ',') + { + std::string path; + packet.GetHexByteString(path); + Error error = Host::SetFilePermissions (path.c_str(), mode); + if (error.Success()) + return SendPacketNoLock ("OK", 2); + else + return SendErrorResponse(error.GetError()); + } + return SendErrorResponse(19); } bool @@ -1065,24 +1166,28 @@ GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packe 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; + if (!path.empty()) + { + if (packet.GetChar() == ',') + { + uint32_t flags = packet.GetHexMaxU32(false, 0); + if (packet.GetChar() == ',') + { + mode_t mode = packet.GetHexMaxU32(false, 0600); + Error error; + int fd = ::open (path.c_str(), flags, mode); + printf ("open('%s', flags=0x%x, mode=%o) fd = %i (%s)\n", path.c_str(), flags, mode, fd, fd == -1 ? strerror(errno) : "<success>"); + 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); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + } + } + return SendErrorResponse(18); } bool @@ -1107,8 +1212,7 @@ GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &pack response.Printf("%i", err); if (save_errno) response.Printf(",%i", save_errno); - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendPacketNoLock(response.GetData(), response.GetSize()); } bool @@ -1116,37 +1220,40 @@ GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &pack { #ifdef _WIN32 // Not implemented on Windows - return false; + return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pRead() unimplemented"); #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 + if (packet.GetChar() == ',') { - response.PutChar(';'); - response.PutEscapedBytes(&buffer[0], bytes_read); + uint64_t count = packet.GetU64(UINT64_MAX); + if (packet.GetChar() == ',') + { + uint64_t offset = packet.GetU64(UINT32_MAX); + if (count == UINT64_MAX) + { + response.Printf("F-1:%i", EINVAL); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + + 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); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } } - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendErrorResponse(21); + #endif } @@ -1154,8 +1261,7 @@ bool GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) { #ifdef _WIN32 - // Not implemented on Windows - return false; + return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pWrite() unimplemented"); #else packet.SetFilePos(::strlen("vFile:pwrite:")); @@ -1163,27 +1269,28 @@ GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &pac 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 + if (packet.GetChar() == ',') { - response.Printf ("-1,%i", EINVAL); + off_t offset = packet.GetU64(UINT32_MAX); + if (packet.GetChar() == ',') + { + 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); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } } - - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendErrorResponse(27); #endif } @@ -1193,19 +1300,20 @@ GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packe 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) + if (!path.empty()) { - response.PutChar(','); - response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() + 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() + } + return SendPacketNoLock(response.GetData(), response.GetSize()); } - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendErrorResponse(22); } bool @@ -1214,16 +1322,17 @@ GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packe 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; + if (!path.empty()) + { + 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()); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(23); } bool @@ -1232,87 +1341,118 @@ GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &pac 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)); + if (!path.empty()) + { + 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'); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(24); +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_symlink (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:symlink:")); + std::string dst, src; + packet.GetHexByteStringTerminatedBy(dst, ','); + packet.GetChar(); // Skip ',' char + packet.GetHexByteString(src); + Error error = Host::Symlink(src.c_str(), dst.c_str()); StreamString response; - response.PutChar('F'); - response.PutChar(','); - if (retcode) - response.PutChar('1'); - else - response.PutChar('0'); - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + response.Printf("F%u,%u", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetData(), response.GetSize()); } bool -GDBRemoteCommunicationServer::Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet) +GDBRemoteCommunicationServer::Handle_vFile_unlink (StringExtractorGDBRemote &packet) { - packet.SetFilePos(::strlen("qPlatform_RunCommand:")); + packet.SetFilePos(::strlen("vFile:unlink:")); + std::string path; + packet.GetHexByteString(path); + Error error = Host::Unlink(path.c_str()); + StreamString response; + response.Printf("F%u,%u", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_shell (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_shell:")); 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()) + if (!path.empty()) { - 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()); + if (packet.GetChar() == ',') + { + // FIXME: add timeout to qPlatform_shell packet + // uint32_t timeout = packet.GetHexMaxU32(false, 32); + uint32_t timeout = 10; + if (packet.GetChar() == ',') + packet.GetHexByteString(working_dir); + int status, signo; + std::string output; + 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()); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } } - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendErrorResponse(24); } bool GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet) { - return false; + return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_Stat() unimplemented"); } bool GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) { - packet.SetFilePos(::strlen("vFile:exists:")); + packet.SetFilePos(::strlen("vFile:MD5:")); 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) + if (!path.empty()) { - response.PutCString("F,"); - response.PutCString("x"); - } - else - { - response.PutCString("F,"); - response.PutHex64(a); - response.PutHex64(b); + 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); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); } - SendPacketNoLock(response.GetData(), response.GetSize()); - return true; + return SendErrorResponse(25); } + diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index 64f6f8d..721ea50 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -26,6 +26,8 @@ class StringExtractorGDBRemote; class GDBRemoteCommunicationServer : public GDBRemoteCommunication { public: + typedef std::map<uint16_t, lldb::pid_t> PortMap; + enum { eBroadcastBitRunPacketSent = kLoUserBroadcastBit @@ -58,30 +60,85 @@ public: // Set both ports to zero to let the platform automatically bind to // a port chosen by the OS. void - SetPortRange (uint16_t lo_port_num, uint16_t hi_port_num) + SetPortMap (PortMap &&port_map) { - 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; + m_port_map = port_map; } - // If we are using a port range, get and update the next port to be used variable. - // Otherwise, just return 0. + //---------------------------------------------------------------------- + // If we are using a port map where we can only use certain ports, + // get the next available port. + // + // If we are using a port map and we are out of ports, return UINT16_MAX + // + // If we aren't using a port map, return 0 to indicate we should bind to + // port 0 and then figure out which port we used. + //---------------------------------------------------------------------- uint16_t - GetAndUpdateNextPort () + GetNextAvailablePort () { - 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; + if (m_port_map.empty()) + return 0; // Bind to port zero and get a port, we didn't have any limitations + + for (auto &pair : m_port_map) + { + if (pair.second == LLDB_INVALID_PROCESS_ID) + { + pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; + return pair.first; + } + } + return UINT16_MAX; } -protected: - //typedef std::map<uint16_t, lldb::pid_t> PortToPIDMap; + bool + AssociatePortWithProcess (uint16_t port, lldb::pid_t pid) + { + PortMap::iterator pos = m_port_map.find(port); + if (pos != m_port_map.end()) + { + pos->second = pid; + return true; + } + return false; + } + + bool + FreePort (uint16_t port) + { + PortMap::iterator pos = m_port_map.find(port); + if (pos != m_port_map.end()) + { + pos->second = LLDB_INVALID_PROCESS_ID; + return true; + } + return false; + } + + bool + FreePortForProcess (lldb::pid_t pid) + { + if (!m_port_map.empty()) + { + for (auto &pair : m_port_map) + { + if (pair.second == pid) + { + pair.second = LLDB_INVALID_PROCESS_ID; + return true; + } + } + } + return false; + } + void + SetPortOffset (uint16_t port_offset) + { + m_port_offset = port_offset; + } + +protected: lldb::thread_t m_async_thread; lldb_private::ProcessLaunchInfo m_process_launch_info; lldb_private::Error m_process_launch_error; @@ -89,11 +146,8 @@ protected: 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; + PortMap m_port_map; + uint16_t m_port_offset; size_t @@ -121,7 +175,10 @@ protected: Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); bool - Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet); + Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet); + + bool + Handle_qPlatform_chmod (StringExtractorGDBRemote &packet); bool Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); @@ -155,6 +212,9 @@ protected: bool Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); + + bool + Handle_qGetWorkingDir (StringExtractorGDBRemote &packet); bool Handle_QStartNoAckMode (StringExtractorGDBRemote &packet); @@ -188,6 +248,12 @@ protected: bool Handle_vFile_Exists (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_symlink (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_unlink (StringExtractorGDBRemote &packet); bool Handle_vFile_Stat (StringExtractorGDBRemote &packet); @@ -196,7 +262,7 @@ protected: Handle_vFile_MD5 (StringExtractorGDBRemote &packet); bool - Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet); + Handle_qPlatform_shell (StringExtractorGDBRemote &packet); private: bool diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp index c4e468f..c291df7 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -153,20 +153,13 @@ bool GDBRemoteRegisterContext::GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { - char packet[64]; - StringExtractorGDBRemote response; - int packet_len = 0; const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; - if (gdb_comm.GetThreadSuffixSupported()) - packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, m_thread.GetProtocolID()); - else - packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg); - assert (packet_len < ((int)sizeof(packet) - 1)); - if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false)) + StringExtractorGDBRemote response; + if (gdb_comm.ReadRegister(m_thread.GetProtocolID(), reg, response)) return PrivateSetRegisterValue (reg, response); - return false; } + bool GDBRemoteRegisterContext::ReadRegisterBytes (const RegisterInfo *reg_info, DataExtractor &data) { @@ -185,93 +178,51 @@ GDBRemoteRegisterContext::ReadRegisterBytes (const RegisterInfo *reg_info, DataE if (!GetRegisterIsValid(reg)) { - Mutex::Locker locker; - if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for read register.")) + if (m_read_all_at_once) { - const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported(); - ProcessSP process_sp (m_thread.GetProcess()); - if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID())) + StringExtractorGDBRemote response; + if (!gdb_comm.ReadAllRegisters(m_thread.GetProtocolID(), response)) + return false; + if (response.IsNormalResponse()) + if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize()) + SetAllRegisterValid (true); + } + else if (reg_info->value_regs) + { + // Process this composite register request by delegating to the constituent + // primordial registers. + + // Index of the primordial register. + bool success = true; + for (uint32_t idx = 0; success; ++idx) { - char packet[64]; - StringExtractorGDBRemote response; - int packet_len = 0; - if (m_read_all_at_once) - { - // Get all registers in one packet - if (thread_suffix_supported) - packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); - else - packet_len = ::snprintf (packet, sizeof(packet), "g"); - assert (packet_len < ((int)sizeof(packet) - 1)); - if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false)) - { - if (response.IsNormalResponse()) - if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize()) - SetAllRegisterValid (true); - } - } - else if (reg_info->value_regs) - { - // Process this composite register request by delegating to the constituent - // primordial registers. - - // Index of the primordial register. - bool success = true; - for (uint32_t idx = 0; success; ++idx) - { - const uint32_t prim_reg = reg_info->value_regs[idx]; - if (prim_reg == LLDB_INVALID_REGNUM) - break; - // We have a valid primordial regsiter as our constituent. - // Grab the corresponding register info. - const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg); - if (prim_reg_info == NULL) - success = false; - else - { - // Read the containing register if it hasn't already been read - if (!GetRegisterIsValid(prim_reg)) - success = GetPrimordialRegister(prim_reg_info, gdb_comm); - } - } - - if (success) - { - // If we reach this point, all primordial register requests have succeeded. - // Validate this composite register. - SetRegisterIsValid (reg_info, true); - } - } + const uint32_t prim_reg = reg_info->value_regs[idx]; + if (prim_reg == LLDB_INVALID_REGNUM) + break; + // We have a valid primordial regsiter as our constituent. + // Grab the corresponding register info. + const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg); + if (prim_reg_info == NULL) + success = false; else { - // Get each register individually - GetPrimordialRegister(reg_info, gdb_comm); + // Read the containing register if it hasn't already been read + if (!GetRegisterIsValid(prim_reg)) + success = GetPrimordialRegister(prim_reg_info, gdb_comm); } } + + if (success) + { + // If we reach this point, all primordial register requests have succeeded. + // Validate this composite register. + SetRegisterIsValid (reg_info, true); + } } else { -#if LLDB_CONFIGURATION_DEBUG - StreamString strm; - gdb_comm.DumpHistory(strm); - Host::SetCrashDescription (strm.GetData()); - assert (!"Didn't get sequence mutex for read register."); -#else - Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS)); - if (log) - { - if (log->GetVerbose()) - { - StreamString strm; - gdb_comm.DumpHistory(strm); - log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\":\n%s", reg_info->name, strm.GetData()); - } - else - { - log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\"", reg_info->name); - } - } -#endif + // Get each register individually + GetPrimordialRegister(reg_info, gdb_comm); } // Make sure we got a valid register value after reading it @@ -488,6 +439,54 @@ GDBRemoteRegisterContext::WriteRegisterBytes (const lldb_private::RegisterInfo * return false; } +bool +GDBRemoteRegisterContext::ReadAllRegisterValues (lldb_private::RegisterCheckpoint ®_checkpoint) +{ + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); + + uint32_t save_id = 0; + if (gdb_comm.SaveRegisterState(thread->GetProtocolID(), save_id)) + { + reg_checkpoint.SetID(save_id); + reg_checkpoint.GetData().reset(); + return true; + } + else + { + reg_checkpoint.SetID(0); // Invalid save ID is zero + return ReadAllRegisterValues(reg_checkpoint.GetData()); + } +} + +bool +GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb_private::RegisterCheckpoint ®_checkpoint) +{ + uint32_t save_id = reg_checkpoint.GetID(); + if (save_id != 0) + { + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); + + return gdb_comm.RestoreRegisterState(m_thread.GetProtocolID(), save_id); + } + else + { + return WriteAllRegisterValues(reg_checkpoint.GetData()); + } +} bool GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h index 7a49d69..38f29bb 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -91,6 +91,12 @@ public: virtual bool WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + virtual bool + ReadAllRegisterValues (lldb_private::RegisterCheckpoint ®_checkpoint); + + virtual bool + WriteAllRegisterValues (const lldb_private::RegisterCheckpoint ®_checkpoint); + virtual uint32_t ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index aff9c7b..7f1fbef 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -820,7 +820,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, const ProcessLaunchInfo &launch_ } const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (10); - int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info.GetArguments().GetConstArgumentVector()); + int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info); if (arg_packet_err == 0) { std::string error_str; @@ -926,15 +926,13 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) // then we aren't actually connected to anything, so try and do the // handshake with the remote GDB server and make sure that goes // alright. - if (!m_gdb_comm.HandshakeWithServer (NULL)) + if (!m_gdb_comm.HandshakeWithServer (&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } - m_gdb_comm.ResetDiscoverableSettings(); - m_gdb_comm.QueryNoAckModeSupported (); m_gdb_comm.GetThreadSuffixSupported (); m_gdb_comm.GetListThreadsInStopReplySupported (); m_gdb_comm.GetHostInfo (); @@ -1158,6 +1156,13 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait } +bool +ProcessGDBRemote::SetExitStatus (int exit_status, const char *cstr) +{ + m_gdb_comm.Disconnect(); + return Process::SetExitStatus (exit_status, cstr); +} + void ProcessGDBRemote::DidAttach () { @@ -2787,6 +2792,7 @@ ProcessGDBRemote::MonitorDebugserverProcess void ProcessGDBRemote::KillDebugserverProcess () { + m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill (m_debugserver_pid, SIGINT); diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index b18ac5b..3524407 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -222,6 +222,13 @@ public: { return m_gdb_comm; } + + //---------------------------------------------------------------------- + // Override SetExitStatus so we can disconnect from the remote GDB server + //---------------------------------------------------------------------- + virtual bool + SetExitStatus (int exit_status, const char *cstr); + protected: friend class ThreadGDBRemote; |