//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" // C Includes #include #include #include #include #include // for mmap #include #include #include // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/InputReader.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/TimeValue.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Utility/PseudoTerminal.h" // Project includes #include "lldb/Host/Host.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Utility/StringExtractorGDBRemote.h" #include "GDBRemoteRegisterContext.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). // We need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this // function and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory (void *p, const char *path) { lldb_private::StreamFile strm; lldb_private::Error error (strm.GetFile().Open(path, lldb_private::File::eOpenOptionWrite | lldb_private::File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm); } } #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; namespace { static PropertyDefinition g_properties[] = { { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; enum { ePropertyPacketTimeout }; class PluginProperties : public Properties { public: static ConstString GetSettingName () { return ProcessGDBRemote::GetPluginNameStatic(); } PluginProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } virtual ~PluginProperties() { } uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP & GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset (new PluginProperties ()); return g_settings_sp; } } // anonymous namespace end static bool rand_initialized = false; // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. #if defined (__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif static inline uint16_t get_random_port () { if (!rand_initialized) { time_t seed = time(NULL); rand_initialized = true; srand(seed); } return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; } lldb_private::ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char * ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset (new ProcessGDBRemote (target, listener)); return process_sp; } bool ProcessGDBRemote::CanDebug (Target &target, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target.GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return exe_module->GetFileSpec().Exists(); } // However, if there is no executable module, we return true since we might be preparing to attach. return true; } //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : Process (target, listener), m_flags (0), m_gdb_comm(false), m_debugserver_pid (LLDB_INVALID_PROCESS_ID), m_last_stop_packet (), m_last_stop_packet_mutex (Mutex::eMutexTypeNormal), m_register_info (), m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_thread (LLDB_INVALID_HOST_THREAD), m_async_thread_state(eAsyncThreadNotStarted), m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), m_thread_ids (), m_continue_c_tids (), 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_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit"); const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(timeout_seconds); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get stranded and // its connection will go away so when it wakes up it will crash. So kill it for sure here. StopAsyncThread(); KillDebugserverProcess(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } void ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; char packet[128]; m_register_info.Clear(); uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false)) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { std::string name; std::string value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // formate { LLDB_INVALID_REGNUM, // GCC reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // GDB reg num reg_num // native register number }, NULL, NULL }; while (response.GetNameColonValue(name, value)) { if (name.compare("name") == 0) { reg_name.SetCString(value.c_str()); } else if (name.compare("alt-name") == 0) { alt_name.SetCString(value.c_str()); } else if (name.compare("bitsize") == 0) { reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT; } else if (name.compare("offset") == 0) { uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0); if (reg_offset != offset) { reg_offset = offset; } } else if (name.compare("encoding") == 0) { const Encoding encoding = Args::StringToEncoding (value.c_str()); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.compare("format") == 0) { Format format = eFormatInvalid; if (Args::StringToFormat (value.c_str(), format, NULL).Success()) reg_info.format = format; else if (value.compare("binary") == 0) reg_info.format = eFormatBinary; else if (value.compare("decimal") == 0) reg_info.format = eFormatDecimal; else if (value.compare("hex") == 0) reg_info.format = eFormatHex; else if (value.compare("float") == 0) reg_info.format = eFormatFloat; else if (value.compare("vector-sint8") == 0) reg_info.format = eFormatVectorOfSInt8; else if (value.compare("vector-uint8") == 0) reg_info.format = eFormatVectorOfUInt8; else if (value.compare("vector-sint16") == 0) reg_info.format = eFormatVectorOfSInt16; else if (value.compare("vector-uint16") == 0) reg_info.format = eFormatVectorOfUInt16; else if (value.compare("vector-sint32") == 0) reg_info.format = eFormatVectorOfSInt32; else if (value.compare("vector-uint32") == 0) reg_info.format = eFormatVectorOfUInt32; else if (value.compare("vector-float32") == 0) reg_info.format = eFormatVectorOfFloat32; else if (value.compare("vector-uint128") == 0) reg_info.format = eFormatVectorOfUInt128; } else if (name.compare("set") == 0) { set_name.SetCString(value.c_str()); } else if (name.compare("gcc") == 0) { reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("dwarf") == 0) { reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("generic") == 0) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister (value.c_str()); } else if (name.compare("container-regs") == 0) { std::pair value_pair; value_pair.second = value; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); if (reg != LLDB_INVALID_REGNUM) value_regs.push_back (reg); } } while (!value_pair.second.empty()); } else if (name.compare("invalidate-regs") == 0) { std::pair value_pair; value_pair.second = value; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); if (reg != LLDB_INVALID_REGNUM) invalidate_regs.push_back (reg); } } while (!value_pair.second.empty()); } } reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } } else { break; } } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. bool from_scratch = (reg_num == 0); const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec remote_arch; if (remote_process_arch.IsValid ()) remote_arch = remote_process_arch; else remote_arch = remote_host_arch; if (!target_arch.IsValid()) { if (remote_arch.IsValid() && remote_arch.GetMachine() == llvm::Triple::arm && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize (); } Error ProcessGDBRemote::WillLaunch (Module* module) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithID (lldb::pid_t pid) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) { Error error (WillLaunchOrAttach ()); if (error.Fail()) return error; error = ConnectToDebugserver (remote_url); if (error.Fail()) return error; StartAsyncThread (); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected // and could now request to launch or attach, or get remote process // listings... SetPrivateState (eStateConnected); } else { // We have a valid process SetID (pid); GetThreadList(); if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) { const StateType state = SetThreadStopInfo (m_last_stop_packet); if (state == eStateStopped) { SetPrivateState (state); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but state was not stopped: %s", pid, remote_url, StateAsCString (state)); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but no stop reply packet was received", pid, remote_url); } if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } return error; } Error ProcessGDBRemote::WillLaunchOrAttach () { Error error; m_stdio_communication.Clear (); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Error ProcessGDBRemote::DoLaunch (Module *exe_module, const ProcessLaunchInfo &launch_info) { Error error; uint32_t launch_flags = launch_info.GetFlags().Get(); const char *stdin_path = NULL; const char *stdout_path = NULL; const char *stderr_path = NULL; const char *working_dir = launch_info.GetWorkingDirectory(); const ProcessLaunchInfo::FileAction *file_action; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); if (file_action) { if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) stdin_path = file_action->GetPath(); } file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); if (file_action) { if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) stdout_path = file_action->GetPath(); } file_action = launch_info.GetFileActionForFD (STDERR_FILENO); if (file_action) { if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) stderr_path = file_action->GetPath(); } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); ObjectFile * object_file = exe_module->GetObjectFile(); if (object_file) { char host_port[128]; snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); char connect_url[128]; snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { error = StartDebugserverProcess (host_port, launch_info); if (error.Fail()) { if (log) log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } error = ConnectToDebugserver (connect_url); } if (error.Success()) { lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. PlatformSP platform_sp (m_target.GetPlatform()); if (platform_sp && platform_sp->IsHost() && !disable_stdio) { const char *slave_name = NULL; if (stdin_path == NULL || stdout_path == NULL || stderr_path == NULL) { if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) slave_name = pty.GetSlaveName (NULL, 0); } if (stdin_path == NULL) stdin_path = slave_name; if (stdout_path == NULL) stdout_path = slave_name; if (stderr_path == NULL) stderr_path = slave_name; } // Set STDIN to /dev/null if we want STDIO disabled or if either // STDOUT or STDERR have been set to something and STDIN hasn't if (disable_stdio || (stdin_path == NULL && (stdout_path || stderr_path))) stdin_path = "/dev/null"; // Set STDOUT to /dev/null if we want STDIO disabled or if either // STDIN or STDERR have been set to something and STDOUT hasn't if (disable_stdio || (stdout_path == NULL && (stdin_path || stderr_path))) stdout_path = "/dev/null"; // Set STDERR to /dev/null if we want STDIO disabled or if either // STDIN or STDOUT have been set to something and STDERR hasn't if (disable_stdio || (stderr_path == NULL && (stdin_path || stdout_path))) stderr_path = "/dev/null"; if (stdin_path) m_gdb_comm.SetSTDIN (stdin_path); if (stdout_path) m_gdb_comm.SetSTDOUT (stdout_path); if (stderr_path) m_gdb_comm.SetSTDERR (stderr_path); m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName()); if (working_dir && working_dir[0]) { m_gdb_comm.SetWorkingDir (working_dir); } // Send the environment and the program + arguments after we connect const Args &environment = launch_info.GetEnvironmentEntries(); if (environment.GetArgumentCount()) { size_t num_environment_entries = environment.GetArgumentCount(); for (size_t i=0; iPrintf("failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess (); return error; } if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) { SetPrivateState (SetThreadStopInfo (m_last_stop_packet)); if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor (pty.ReleaseMasterFileDescriptor()); } } } else { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Error ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) { Error error; // Sleep and wait a bit for debugserver to start to listen... std::unique_ptr conn_ap(new ConnectionFileDescriptor()); if (conn_ap.get()) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection (conn_ap.release()); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } retry_count++; if (retry_count >= max_retry_count) break; usleep (100000); } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer (NULL)) { 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 (); m_gdb_comm.GetVContSupported ('c'); m_gdb_comm.GetVAttachOrWaitSupported(); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse (GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) 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 ArchSpec gdb_remote_arch = m_gdb_comm.GetHostArchitecture(); // See if the GDB server supports the qProcessInfo packet, if so // prefer that over the Host information as it will be more specific // to our process. if (m_gdb_comm.GetProcessArchitecture().IsValid()) gdb_remote_arch = m_gdb_comm.GetProcessArchitecture(); if (gdb_remote_arch.IsValid()) { ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared libraries // it has, so we really need to take the remote host architecture as our // defacto architecture in this case. if (gdb_remote_arch.GetMachine() == llvm::Triple::arm && gdb_remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) { target_arch = gdb_remote_arch; } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = gdb_remote_arch.GetTriple(); llvm::Triple &target_triple = target_arch.GetTriple(); if (target_triple.getVendorName().size() == 0) { target_triple.setVendor (remote_triple.getVendor()); if (target_triple.getOSName().size() == 0) { target_triple.setOS (remote_triple.getOS()); if (target_triple.getEnvironmentName().size() == 0) target_triple.setEnvironment (remote_triple.getEnvironment()); } } } } else { // The target doesn't have a valid architecture yet, set it from // the architecture we got from the remote GDB server target_arch = gdb_remote_arch; } } } } void ProcessGDBRemote::DidLaunch () { DidLaunchOrAttach (); } Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid) { ProcessAttachInfo attach_info; return DoAttachToProcessWithID(attach_pid, attach_info); } Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Error error; // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { char host_port[128]; snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); char connect_url[128]; snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); error = StartDebugserverProcess (host_port, attach_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "unable to launch " DEBUGSERVER_BASENAME; SetExitStatus (-1, error_string); } else { error = ConnectToDebugserver (connect_url); } } if (error.Success()) { char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } } return error; } size_t ProcessGDBRemote::AttachInputReaderCallback ( void *baton, InputReader *reader, lldb::InputReaderAction notification, const char *bytes, size_t bytes_len ) { if (notification == eInputReaderGotToken) { ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton; if (gdb_process->m_waiting_for_attach) gdb_process->m_waiting_for_attach = false; reader->SetIsDone(true); return 1; } return 0; } Error ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info) { Error error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { char host_port[128]; snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); char connect_url[128]; snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); error = StartDebugserverProcess (host_port, attach_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "unable to launch " DEBUGSERVER_BASENAME; SetExitStatus (-1, error_string); } else { error = ConnectToDebugserver (connect_url); } } if (error.Success()) { StreamString packet; if (wait_for_launch) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString ("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString ("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); } } return error; } void ProcessGDBRemote::DidAttach () { DidLaunchOrAttach (); } Error ProcessGDBRemote::WillResume () { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); return Error(); } Error ProcessGDBRemote::DoResume () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::Resume()"); Listener listener ("gdb-remote.resume-packet-sent"); if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport ()) { if (m_continue_c_tids.size() == num_threads) { // All threads are continuing, just send a "c" packet continue_packet.PutCString ("c"); } else { continue_packet.PutCString ("vCont"); if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported ('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported ('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported ('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported ('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (continue_packet_error) continue_packet.GetString().clear(); } } else continue_packet_error = true; if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont // packet that wasn't supported by the remote GDB server. // We need to try and make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); continue_packet.PutChar ('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); continue_packet.PutChar ('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { if ((num_continue_C_tids + num_continue_c_tids) == num_threads && num_continue_C_tids > 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue // accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i=1; i 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); continue_packet.PutChar ('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 1 && num_continue_S_tids == 0 ) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); continue_packet.PutChar ('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i=1; iPrintf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); return error; } m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); if (listener.WaitForEvent (&timeout, event_sp) == false) { error.SetErrorString("Resume timed out."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs (&m_async_broadcaster)) { error.SetErrorString ("Broadcast continue, but the async thread was killed before we got an ack back."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::ClearThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); } bool ProcessGDBRemote::UpdateThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList ()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i=0; iGetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Making new thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, thread_sp.get(), thread_sp->GetID()); } else { if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Found old thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, thread_sp.get(), thread_sp->GetID()); } new_thread_list.AddThread(thread_sp); } } // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i=0; iGetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } return true; } StateType ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) { stop_packet.SetFilePos (0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we // need to clear our thread lists and also know to rebuild our dynamic // register info before we lookup and threads and populate the expedited // register values so we need to know this right away so we can cleanup // and update our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid != LLDB_INVALID_PROCESS_ID) SetID (pid); } BuildDynamicRegisterInfo (true); } // Stop with signal and thread info const uint8_t signo = stop_packet.GetHexU8(); std::string name; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ThreadSP thread_sp; ThreadGDBRemote *gdb_thread = NULL; while (stop_packet.GetNameColonValue(name, value)) { if (name.compare("metype") == 0) { // exception type in big endian hex exc_type = Args::StringToUInt32 (value.c_str(), 0, 16); } else if (name.compare("medata") == 0) { // exception data in big endian hex exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16)); } else if (name.compare("thread") == 0) { // thread in big endian hex lldb::tid_t tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us Mutex::Locker locker (m_thread_list_real.GetMutex ()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp.reset (new ThreadGDBRemote (*this, tid)); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, thread_sp.get(), thread_sp->GetID()); m_thread_list_real.AddThread(thread_sp); } gdb_thread = static_cast (thread_sp.get()); } else if (name.compare("threads") == 0) { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); value.erase(0, comma_pos + 1); } tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); } else if (name.compare("hexname") == 0) { StringExtractor name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); thread_name.swap (value); } else if (name.compare("name") == 0) { thread_name.swap (value); } else if (name.compare("qaddr") == 0) { thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16); } else if (name.compare("reason") == 0) { reason.swap(value); } else if (name.compare("description") == 0) { StringExtractor desc_extractor; // Swap "value" over into "name_extractor" desc_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString (thread_name); } else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1])) { // We have a register number that contains an expedited // register value. Lets supply this register to our thread // so it won't have to go and read it. if (gdb_thread) { uint32_t reg = Args::StringToUInt32 (name.c_str(), UINT32_MAX, 16); if (reg != UINT32_MAX) { StringExtractor reg_value_extractor; // Swap "value" over into "reg_value_extractor" reg_value_extractor.GetStringRef().swap(value); if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor)) { Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'", name.c_str(), reg, reg, reg_value_extractor.GetStringRef().c_str(), stop_packet.GetStringRef().c_str()); } } } } } if (thread_sp) { // Clear the stop info just in case we don't set it to anything thread_sp->SetStopInfo (StopInfoSP()); gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str()); if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread (thread_sp.get())) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } } else if (reason.compare("trap") == 0) { // Let the trap just use the standard signal stop reason below... } else if (reason.compare("watchpoint") == 0) { break_id_t watch_id = LLDB_INVALID_WATCH_ID; // TODO: locate the watchpoint somehow... thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); handled = true; } else if (reason.compare("exception") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); handled = true; } else if (reason.compare("exec") == 0) { did_exec = true; thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } if (signo && did_exec == false) { if (signo == SIGTRAP) { // 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(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread (thread_sp.get())) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of the trace. If we were // not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); else thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo)); } } if (!handled) thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo)); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); if (stop_info_sp) { stop_info_sp->SetDescription (description.c_str()); } else { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); } } } } return eStateStopped; } break; case 'W': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. SetThreadStopInfo (m_last_stop_packet); // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } Error ProcessGDBRemote::DoHalt (bool &caused_stop) { Error error; bool timed_out = false; Mutex::Locker locker; if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close // our file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else { if (!m_gdb_comm.SendInterrupt (locker, 2, timed_out)) { if (timed_out) error.SetErrorString("timed out sending interrupt packet"); else error.SetErrorString("unknown error sending interrupt packet"); } caused_stop = m_gdb_comm.GetInterruptWasSent (); } return error; } Error ProcessGDBRemote::DoDetach(bool keep_stopped) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); DisableAllBreakpointSites (); m_thread_list.DiscardThreadPlans(); error = m_gdb_comm.Detach (keep_stopped); if (log) { if (error.Success()) log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully"); else log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread (); SetPrivateState (eStateDetached); ResumePrivateStateThread(); //KillDebugserverProcess (); return error; } Error ProcessGDBRemote::DoDestroy () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the process // they are debugging properly. If the process is sitting at a breakpoint or an exception, // this can cause problems with restarting. So we check to see if any of our threads are stopped // at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN // destroy it again. // // Note, we don't have a good way to test the version of debugserver, but I happen to know that // the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of // the debugservers with this bug are equal. There really should be a better way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and // get called here to destroy again and we're still at a breakpoint or exception, then we should // just do the straight-forward kill. // // And of course, if we weren't able to stop the process by the time we get here, it isn't // necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy()Tried resuming to destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled Process::Destroy, // but we really need it to happen here and it doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); { Mutex::Locker locker(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } if (stop_looks_like_crash) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to suspend all the threads // before resuming so they won't get into more trouble. Sadly, for the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do // have to run the risk of letting those threads proceed a bit. { Mutex::Locker locker(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume (); return Destroy(); } } } } // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (3); if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async)) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { SetLastStopPacket (response); ClearThreadIDList (); exit_status = response.GetHexU8(); } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - got unexpected response to k packet: %s", response.GetStringRef().c_str()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(response.GetStringRef()); } } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } m_gdb_comm.SetPacketTimeout(old_packet_timeout); } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign ("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread (); KillDebugserverProcess (); return error; } void ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) { lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex); const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo (true); m_gdb_comm.ResetDiscoverableSettings(); } m_last_stop_packet = response; } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessGDBRemote::IsAlive () { return m_gdb_comm.IsConnected() && m_private_state.GetValue() != eStateExited; } addr_t ProcessGDBRemote::GetImageInfoAddress() { return m_gdb_comm.GetShlibInfoAddr(); } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) { if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "m%" PRIx64 ",%" PRIx64, (uint64_t)addr, (uint64_t)size); assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, true)) { if (response.IsNormalResponse()) { error.Clear(); return response.GetHexBytes(buf, size, '\xdd'); } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support reading memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } size_t ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) { if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } StreamString packet; packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, true)) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support writing memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory write packet '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) { addr_t allocated_addr = LLDB_INVALID_ADDRESS; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: case eLazyBoolYes: allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes) return allocated_addr; case eLazyBoolNo: // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else allocated_addr = LLDB_INVALID_ADDRESS; break; } if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); else error.Clear(); return allocated_addr; } Error ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, MemoryRegionInfo ®ion_info) { Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) { Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) { Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after)); return error; } Error ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) { Error error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString ("tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory (addr)) error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); break; case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase (pos); else error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } return 0; } Error ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); const addr_t addr = bp_site->GetLoadAddress(); if (log) log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { if (log) log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } else { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); if (bp_site->HardwarePreferred()) { // Try and set hardware breakpoint, and if that fails, fall through // and set a software breakpoint? if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointHardware)) { if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0) { bp_site->SetEnabled(true); bp_site->SetType (BreakpointSite::eHardware); return error; } } } if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointSoftware)) { if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) { bp_site->SetEnabled(true); bp_site->SetType (BreakpointSite::eExternal); return error; } } return EnableSoftwareBreakpoint (bp_site); } if (log) { const char *err_string = error.AsCString(); log->Printf ("ProcessGDBRemote::EnableBreakpointSite () error for breakpoint at 0x%8.8" PRIx64 ": %s", bp_site->GetLoadAddress(), err_string ? err_string : "NULL"); } // We shouldn't reach here on a successful breakpoint enable... if (error.Success()) error.SetErrorToGenericError(); return error; } Error ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint (bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; } if (error.Success()) bp_site->SetEnabled(false); } else { if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType (Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Error ProcessGDBRemote::EnableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket (type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Error ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. // This disabling attempt might come from the user-supplied actions, we'll route it in order for // the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_flags = 0; m_thread_list_real.Clear(); m_thread_list.Clear(); } Error ProcessGDBRemote::DoSignal (int signo) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal (signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Error ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url) { ProcessLaunchInfo launch_info; return StartDebugserverProcess(debugserver_url, launch_info); } Error ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url, const ProcessInfo &process_info) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") { Error error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; char debugserver_path[PATH_MAX]; FileSpec &debugserver_file_spec = debugserver_launch_info.GetExecutableFile(); // Always check to see if we have an environment override for the path // to the debugserver to use and use it if we do. const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); if (env_debugserver_path) debugserver_file_spec.SetFile (env_debugserver_path, false); else debugserver_file_spec = g_debugserver_file_spec; bool debugserver_exists = debugserver_file_spec.Exists(); if (!debugserver_exists) { // The debugserver binary is in the LLDB.framework/Resources // directory. if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec)) { debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME); debugserver_exists = debugserver_file_spec.Exists(); if (debugserver_exists) { g_debugserver_file_spec = debugserver_file_spec; } else { g_debugserver_file_spec.Clear(); debugserver_file_spec.Clear(); } } } if (debugserver_exists) { debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); m_stdio_communication.Clear(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Args &debugserver_args = debugserver_launch_info.GetArguments(); char arg_cstr[PATH_MAX]; // Start args with "debugserver /file/path -r --" debugserver_args.AppendArgument(debugserver_path); debugserver_args.AppendArgument(debugserver_url); // use native registers, not the GDB registers debugserver_args.AppendArgument("--native-regs"); // make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver debugserver_args.AppendArgument("--setsid"); const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); if (env_debugserver_log_file) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); debugserver_args.AppendArgument(arg_cstr); } const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); if (env_debugserver_log_flags) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); debugserver_args.AppendArgument(arg_cstr); } // debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); // debugserver_args.AppendArgument("--log-flags=0x802e0e"); // We currently send down all arguments, attach pids, or attach // process names in dedicated GDB server packets, so we don't need // to pass them as arguments. This is currently because of all the // things we need to setup prior to launching: the environment, // current working dir, file actions, etc. #if 0 // Now append the program arguments if (inferior_argv) { // Terminate the debugserver args so we can now append the inferior args debugserver_args.AppendArgument("--"); for (int i = 0; inferior_argv[i] != NULL; ++i) debugserver_args.AppendArgument (inferior_argv[i]); } else if (attach_pid != LLDB_INVALID_PROCESS_ID) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); debugserver_args.AppendArgument (arg_cstr); } else if (attach_name && attach_name[0]) { if (wait_for_launch) debugserver_args.AppendArgument ("--waitfor"); else debugserver_args.AppendArgument ("--attach"); debugserver_args.AppendArgument (attach_name); } #endif ProcessLaunchInfo::FileAction file_action; // Close STDIN, STDOUT and STDERR. We might need to redirect them // to "/dev/null" if we run into any problems. file_action.Close (STDIN_FILENO); debugserver_launch_info.AppendFileAction (file_action); file_action.Close (STDOUT_FILENO); debugserver_launch_info.AppendFileAction (file_action); file_action.Close (STDERR_FILENO); debugserver_launch_info.AppendFileAction (file_action); if (log) { StreamString strm; debugserver_args.Dump (&strm); log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData()); } debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); debugserver_launch_info.SetUserID(process_info.GetUserID()); error = Host::LaunchProcess(debugserver_launch_info); if (error.Success ()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (error.Fail() || log) error.PutToLog(log, "Host::LaunchProcess (launch_info) => pid=%" PRIu64 ", path='%s'", m_debugserver_pid, debugserver_path); } else { error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME); } if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) StartAsyncThread (); } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess ( void *callback_baton, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // The baton is a "ProcessGDBRemote *". Now this class might be gone // and might not exist anymore, so we need to carefully try to get the // target for this process first since we have a race condition when // we are done running between getting the notice that the inferior // process has died and the debugserver that was debugging this process. // In our test suite, we are also continually running process after // process, so we must be very careful to make sure: // 1 - process object hasn't been deleted already // 2 - that a new process object hasn't been recreated in its place // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton; // Get a shared pointer to the target that has a matching process pointer. // This target could be gone, or the target could already have a new process // object inside of it TargetSP target_sp (Debugger::FindTargetWithProcess(process)); if (log) log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status); if (target_sp) { // We found a process in a target that matches, but another thread // might be in the process of launching a new process that will // soon replace it, so get a shared pointer to the process so we // can keep it alive. ProcessSP process_sp (target_sp->GetProcessSP()); // Now we have a shared pointer to the process that can't go away on us // so we now make sure it was the same as the one passed in, and also make // sure that our previous "process *" didn't get deleted and have a new // "process *" created in its place with the same pointer. To verify this // we make sure the process has our debugserver process ID. If we pass all // of these tests, then we are sure that this process is the one we were // looking for. if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid) { // Sleep for a half a second to make sure our inferior process has // time to set its exit status before we set it incorrectly when // both the debugserver and the inferior process shut down. usleep (500000); // If our process hasn't yet exited, debugserver might have died. // If the process did exit, the we are reaping it. const StateType state = process->GetState(); if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process->GetUnixSignals().GetSignalAsCString (signo); if (signal_cstr) ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process->SetExitStatus (-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote // know that it no longer has a debugserver instance process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } return true; } void ProcessGDBRemote::KillDebugserverProcess () { if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { ::kill (m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static bool g_initialized = false; if (g_initialized == false) { g_initialized = true; PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); Log::Callbacks log_callbacks = { ProcessGDBRemoteLog::DisableLog, ProcessGDBRemoteLog::EnableLog, ProcessGDBRemoteLog::ListLogCategories }; Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks); } } void ProcessGDBRemote::DebuggerInitialize (lldb_private::Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin (debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString ("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); if (m_async_thread_state == eAsyncThreadNotStarted) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). m_async_thread = Host::ThreadCreate ("", ProcessGDBRemote::AsyncThread, this, NULL); if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) { m_async_thread_state = eAsyncThreadRunning; return true; } else return false; } else { // Somebody tried to start the async thread while it was either being started or stopped. If the former, and // it started up successfully, then say all's well. Otherwise it is an error, since we aren't going to restart it. if (log) log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); if (m_async_thread_state == eAsyncThreadRunning) return true; else return false; } } void ProcessGDBRemote::StopAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); if (m_async_thread_state == eAsyncThreadRunning) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) { Host::ThreadJoin (m_async_thread, NULL, NULL); } m_async_thread_state = eAsyncThreadDone; } else { if (log) log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); } } void * ProcessGDBRemote::AsyncThread (void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote*) arg; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); Listener listener ("ProcessGDBRemote::AsyncThread"); EventSP event_sp; const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit); bool done = false; while (!done) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (listener.WaitForEvent (NULL, event_sp)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes (); const size_t continue_cstr_len = continue_packet->GetByteSize (); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr (continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. // The thread ID list might be contained within the "response", or the stop reply packet that // caused the stop. So clear it now before we give the stop reply packet to the process // using the process->SetLastStopPacket()... process->ClearThreadIDList (); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket (response); process->SetPrivateState (stop_state); break; case eStateExited: process->SetLastStopPacket (response); process->ClearThreadIDList(); response.SetFilePos(1); process->SetExitStatus(response.GetHexU8(), NULL); done = true; break; case eStateInvalid: process->SetExitStatus(-1, "lost connection"); break; default: process->SetPrivateState (stop_state); break; } } } break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs (&process->m_gdb_comm)) { if (event_type & Communication::eBroadcastBitReadThreadDidExit) { process->SetExitStatus (-1, "lost connection"); done = true; } } } else { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } } if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); process->m_async_thread = LLDB_INVALID_HOST_THREAD; 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 &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to fire up a debugserver // // process and ask it for the list of processes. But if we are local, we can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } // else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, lldb_private::StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp (m_target.GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(m_target); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback (ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { if (log) log->Printf("Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != NULL; } bool ProcessGDBRemote::StopNoticingNewThreads() { Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } lldb_private::DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet history", "Dumps the packet history buffer. ", NULL) { } ~CommandObjectProcessGDBRemotePacketHistory () { } bool DoExecute (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus (eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet send", "Send a custom packet through the GDB remote protocol and print the answer. " "The packet header and footer will automatically be added to the packet prior to sending and stripped from the result.", NULL) { } ~CommandObjectProcessGDBRemotePacketSend () { } bool DoExecute (Args& command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat ("'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i=0; iGetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); } if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol and print the response." "The argument passed to this command will be hex encoded into a valid 'qRcmd' packet, sent and the response will be printed.", NULL) { } ~CommandObjectProcessGDBRemotePacketMonitor () { } bool DoExecute (const char *command, CommandReturnObject &result) { if (command == NULL || command[0] == '\0') { result.AppendErrorWithFormat ("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); const char *packet_cstr = packet.GetString().c_str(); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); const std::string &response_str = response.GetStringRef(); if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", NULL) { LoadSubCommand ("history", CommandObjectSP (new CommandObjectProcessGDBRemotePacketHistory (interpreter))); LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter))); LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter))); } ~CommandObjectProcessGDBRemotePacket () { } }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin", "A set of commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter))); } ~CommandObjectMultiwordProcessGDBRemote () { } }; CommandObject * ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); }