diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 1324 |
1 files changed, 675 insertions, 649 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index b8a737586eb4..6ed3f4fc3f59 100644 --- a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -43,7 +43,6 @@ #include "lldb/Host/HostThread.h" #include "lldb/Host/PosixApi.h" #include "lldb/Host/PseudoTerminal.h" -#include "lldb/Host/StringConvert.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -106,7 +105,7 @@ namespace lldb { // and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { auto file = FileSystem::Instance().Open( - FileSpec(path), File::eOpenOptionWrite | File::eOpenOptionCanCreate); + FileSpec(path), File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate); if (!file) { llvm::consumeError(file.takeError()); return; @@ -129,7 +128,7 @@ enum { class PluginProperties : public Properties { public: static ConstString GetSettingName() { - return ProcessGDBRemote::GetPluginNameStatic(); + return ConstString(ProcessGDBRemote::GetPluginNameStatic()); } PluginProperties() : Properties() { @@ -168,13 +167,9 @@ public: } }; -typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; - -static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { - static ProcessKDPPropertiesSP g_settings_sp; - if (!g_settings_sp) - g_settings_sp = std::make_shared<PluginProperties>(); - return g_settings_sp; +static PluginProperties &GetGlobalPluginProperties() { + static PluginProperties g_settings; + return g_settings; } } // namespace @@ -191,12 +186,7 @@ static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { #define HIGH_PORT (49151u) #endif -ConstString ProcessGDBRemote::GetPluginNameStatic() { - static ConstString g_name("gdb-remote"); - return g_name; -} - -const char *ProcessGDBRemote::GetPluginDescriptionStatic() { +llvm::StringRef ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } @@ -216,7 +206,7 @@ ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, } std::chrono::seconds ProcessGDBRemote::GetPacketTimeout() { - return std::chrono::seconds(GetGlobalPluginProperties()->GetPacketTimeout()); + return std::chrono::seconds(GetGlobalPluginProperties().GetPacketTimeout()); } bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, @@ -254,8 +244,7 @@ bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), - m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(), - m_register_info_sp(nullptr), + m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_register_info_sp(nullptr), m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"), m_async_listener_sp( Listener::MakeListener("lldb.process.gdb-remote.async-listener")), @@ -267,7 +256,8 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_command_sp(), m_breakpoint_pc_offset(0), m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false), - m_allow_flash_writes(false), m_erased_flash_ranges() { + m_allow_flash_writes(false), m_erased_flash_ranges(), + m_vfork_in_progress(false) { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, @@ -305,12 +295,12 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, } const uint64_t timeout_seconds = - GetGlobalPluginProperties()->GetPacketTimeout(); + GetGlobalPluginProperties().GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); m_use_g_packet_for_reading = - GetGlobalPluginProperties()->GetUseGPacketForReading(); + GetGlobalPluginProperties().GetUseGPacketForReading(); } // Destructor @@ -331,11 +321,6 @@ ProcessGDBRemote::~ProcessGDBRemote() { KillDebugserverProcess(); } -// PluginInterface -ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } - -uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } - bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = @@ -384,20 +369,14 @@ bool ProcessGDBRemote::ParsePythonTargetDefinition( } static size_t SplitCommaSeparatedRegisterNumberString( - const llvm::StringRef &comma_separated_regiter_numbers, + const llvm::StringRef &comma_separated_register_numbers, std::vector<uint32_t> ®nums, int base) { regnums.clear(); - std::pair<llvm::StringRef, llvm::StringRef> value_pair; - value_pair.second = comma_separated_regiter_numbers; - do { - value_pair = value_pair.second.split(','); - if (!value_pair.first.empty()) { - uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(), - LLDB_INVALID_REGNUM, base); - if (reg != LLDB_INVALID_REGNUM) - regnums.push_back(reg); - } - } while (!value_pair.second.empty()); + for (llvm::StringRef x : llvm::split(comma_separated_register_numbers, ',')) { + uint32_t reg; + if (llvm::to_integer(x, reg, base)) + regnums.push_back(reg); + } return regnums.size(); } @@ -412,7 +391,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { // timeout is and can see it. const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout > std::chrono::seconds(0)) { - GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); + GetGlobalPluginProperties().SetPacketTimeout(host_packet_timeout.count()); } // Register info search order: @@ -422,7 +401,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = - GetGlobalPluginProperties()->GetTargetDefinitionFile(); + GetGlobalPluginProperties().GetTargetDefinitionFile(); if (!FileSystem::Instance().Exists(target_definition_fspec)) { // If the filename doesn't exist, it may be a ~ not having been expanded - // try to resolve it. @@ -457,7 +436,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { return; char packet[128]; - uint32_t reg_offset = LLDB_INVALID_INDEX32; + std::vector<DynamicRegisterInfo::Register> registers; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; @@ -473,53 +452,25 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { if (response_type == StringExtractorGDBRemote::eResponse) { llvm::StringRef name; llvm::StringRef value; - ConstString reg_name; - ConstString alt_name; - ConstString set_name; - std::vector<uint32_t> value_regs; - std::vector<uint32_t> invalidate_regs; - std::vector<uint8_t> dwarf_opcode_bytes; - RegisterInfo reg_info = { - nullptr, // Name - nullptr, // Alt name - 0, // byte size - reg_offset, // offset - eEncodingUint, // encoding - eFormatHex, // format - { - LLDB_INVALID_REGNUM, // eh_frame reg num - LLDB_INVALID_REGNUM, // DWARF reg num - LLDB_INVALID_REGNUM, // generic reg num - reg_num, // process plugin reg num - reg_num // native register number - }, - nullptr, - nullptr, - nullptr, // Dwarf expression opcode bytes pointer - 0 // Dwarf expression opcode bytes length - }; + DynamicRegisterInfo::Register reg_info; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { - reg_name.SetString(value); + reg_info.name.SetString(value); } else if (name.equals("alt-name")) { - alt_name.SetString(value); + reg_info.alt_name.SetString(value); } else if (name.equals("bitsize")) { - value.getAsInteger(0, reg_info.byte_size); - reg_info.byte_size /= CHAR_BIT; + if (!value.getAsInteger(0, reg_info.byte_size)) + reg_info.byte_size /= CHAR_BIT; } else if (name.equals("offset")) { - if (value.getAsInteger(0, reg_offset)) - reg_offset = UINT32_MAX; + value.getAsInteger(0, reg_info.byte_offset); } else if (name.equals("encoding")) { const Encoding encoding = Args::StringToEncoding(value); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.equals("format")) { - Format format = eFormatInvalid; - if (OptionArgParser::ToFormat(value.str().c_str(), format, nullptr) + if (!OptionArgParser::ToFormat(value.str().c_str(), reg_info.format, nullptr) .Success()) - reg_info.format = format; - else { reg_info.format = llvm::StringSwitch<Format>(value) .Case("binary", eFormatBinary) @@ -536,58 +487,23 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { .Case("vector-uint64", eFormatVectorOfUInt64) .Case("vector-uint128", eFormatVectorOfUInt128) .Default(eFormatInvalid); - } } else if (name.equals("set")) { - set_name.SetString(value); + reg_info.set_name.SetString(value); } else if (name.equals("gcc") || name.equals("ehframe")) { - if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame])) - reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; + value.getAsInteger(0, reg_info.regnum_ehframe); } else if (name.equals("dwarf")) { - if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF])) - reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; + value.getAsInteger(0, reg_info.regnum_dwarf); } else if (name.equals("generic")) { - reg_info.kinds[eRegisterKindGeneric] = - Args::StringToGenericRegister(value); + reg_info.regnum_generic = Args::StringToGenericRegister(value); } else if (name.equals("container-regs")) { - SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); + SplitCommaSeparatedRegisterNumberString(value, reg_info.value_regs, 16); } else if (name.equals("invalidate-regs")) { - SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); - } else if (name.equals("dynamic_size_dwarf_expr_bytes")) { - size_t dwarf_opcode_len = value.size() / 2; - assert(dwarf_opcode_len > 0); - - dwarf_opcode_bytes.resize(dwarf_opcode_len); - reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; - - StringExtractor opcode_extractor(value); - uint32_t ret_val = - opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); - assert(dwarf_opcode_len == ret_val); - UNUSED_IF_ASSERT_DISABLED(ret_val); - reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); + SplitCommaSeparatedRegisterNumberString(value, reg_info.invalidate_regs, 16); } } - reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); - reg_offset = LLDB_INVALID_INDEX32; - 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(); - } - - reg_info.name = reg_name.AsCString(); - // We have to make a temporary ABI here, and not use the GetABI because - // this code gets called in DidAttach, when the target architecture - // (and consequently the ABI we'll get from the process) may be wrong. - if (ABISP abi_sp = ABI::FindPlugin(shared_from_this(), arch_to_use)) - abi_sp->AugmentRegisterInfo(reg_info); - - m_register_info_sp->AddRegister(reg_info, reg_name, alt_name, set_name); + registers.push_back(reg_info); } else { break; // ensure exit before reg_num is incremented } @@ -596,31 +512,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { } } - if (m_register_info_sp->GetNumRegisters() > 0) { - m_register_info_sp->Finalize(GetTarget().GetArchitecture()); - return; - } - - // We didn't get anything if the accumulated reg_num is zero. See if we are - // debugging ARM and fill with a hard coded register set until we can get an - // updated debugserver down on the devices. On the other hand, if the - // accumulated reg_num is positive, see if we can add composite registers to - // the existing primordial ones. - bool from_scratch = (m_register_info_sp->GetNumRegisters() == 0); - - if (!target_arch.IsValid()) { - if (arch_to_use.IsValid() && - (arch_to_use.GetMachine() == llvm::Triple::arm || - arch_to_use.GetMachine() == llvm::Triple::thumb) && - arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) - m_register_info_sp->HardcodeARMRegisters(from_scratch); - } else if (target_arch.GetMachine() == llvm::Triple::arm || - target_arch.GetMachine() == llvm::Triple::thumb) { - m_register_info_sp->HardcodeARMRegisters(from_scratch); - } - - // At this point, we can finalize our register info. - m_register_info_sp->Finalize(GetTarget().GetArchitecture()); + AddRemoteRegisters(registers, arch_to_use); } Status ProcessGDBRemote::WillLaunch(lldb_private::Module *module) { @@ -665,10 +557,6 @@ Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) { if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); - // '?' Packets must be handled differently in non-stop mode - if (GetTarget().GetNonStopModeEnabled()) - HandleStopReplySequence(); - Target &target = GetTarget(); if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { @@ -721,14 +609,6 @@ Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) { __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); - if (error.Success()) { - PlatformSP platform_sp = GetTarget().GetPlatform(); - if (platform_sp && platform_sp->IsConnected()) - SetUnixSignals(platform_sp->GetUnixSignals()); - else - SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); - } - return error; } @@ -799,146 +679,133 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module, // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); - ObjectFile *object_file = exe_module->GetObjectFile(); - if (object_file) { - error = EstablishConnectionIfNeeded(launch_info); - if (error.Success()) { - PseudoTerminal pty; - const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; - - PlatformSP platform_sp(GetTarget().GetPlatform()); - if (disable_stdio) { - // set to /dev/null unless redirected to a file above - if (!stdin_file_spec) - stdin_file_spec.SetFile(FileSystem::DEV_NULL, - FileSpec::Style::native); - if (!stdout_file_spec) - stdout_file_spec.SetFile(FileSystem::DEV_NULL, - FileSpec::Style::native); - if (!stderr_file_spec) - stderr_file_spec.SetFile(FileSystem::DEV_NULL, - FileSpec::Style::native); - } else if (platform_sp && platform_sp->IsHost()) { - // If the debugserver is local and we aren't disabling STDIO, lets use - // a pseudo terminal to instead of relying on the 'O' packets for stdio - // since 'O' packets can really slow down debugging if the inferior - // does a lot of output. - if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && - !errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) { - FileSpec secondary_name(pty.GetSecondaryName()); - - if (!stdin_file_spec) - stdin_file_spec = secondary_name; - - if (!stdout_file_spec) - stdout_file_spec = secondary_name; - - if (!stderr_file_spec) - stderr_file_spec = secondary_name; - } - LLDB_LOGF( - log, - "ProcessGDBRemote::%s adjusted STDIO paths for local platform " - "(IsHost() is true) using secondary: stdin=%s, stdout=%s, " - "stderr=%s", - __FUNCTION__, - stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", - stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", - stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); - } - - LLDB_LOGF(log, - "ProcessGDBRemote::%s final STDIO paths after all " - "adjustments: stdin=%s, stdout=%s, stderr=%s", - __FUNCTION__, - stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", - stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", - stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); - - if (stdin_file_spec) - m_gdb_comm.SetSTDIN(stdin_file_spec); - if (stdout_file_spec) - m_gdb_comm.SetSTDOUT(stdout_file_spec); - if (stderr_file_spec) - m_gdb_comm.SetSTDERR(stderr_file_spec); + error = EstablishConnectionIfNeeded(launch_info); + if (error.Success()) { + PseudoTerminal pty; + const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; - m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); - m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (disable_stdio) { + // set to /dev/null unless redirected to a file above + if (!stdin_file_spec) + stdin_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + if (!stdout_file_spec) + stdout_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + if (!stderr_file_spec) + stderr_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + } else if (platform_sp && platform_sp->IsHost()) { + // If the debugserver is local and we aren't disabling STDIO, lets use + // a pseudo terminal to instead of relying on the 'O' packets for stdio + // since 'O' packets can really slow down debugging if the inferior + // does a lot of output. + if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && + !errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) { + FileSpec secondary_name(pty.GetSecondaryName()); - m_gdb_comm.SendLaunchArchPacket( - GetTarget().GetArchitecture().GetArchitectureName()); + if (!stdin_file_spec) + stdin_file_spec = secondary_name; - const char *launch_event_data = launch_info.GetLaunchEventData(); - if (launch_event_data != nullptr && *launch_event_data != '\0') - m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); + if (!stdout_file_spec) + stdout_file_spec = secondary_name; - if (working_dir) { - m_gdb_comm.SetWorkingDir(working_dir); + if (!stderr_file_spec) + stderr_file_spec = secondary_name; } + LLDB_LOGF( + log, + "ProcessGDBRemote::%s adjusted STDIO paths for local platform " + "(IsHost() is true) using secondary: stdin=%s, stdout=%s, " + "stderr=%s", + __FUNCTION__, + stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", + stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", + stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); + } - // Send the environment and the program + arguments after we connect - m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); + LLDB_LOGF(log, + "ProcessGDBRemote::%s final STDIO paths after all " + "adjustments: stdin=%s, stdout=%s, stderr=%s", + __FUNCTION__, + stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", + stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", + stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); + + if (stdin_file_spec) + m_gdb_comm.SetSTDIN(stdin_file_spec); + if (stdout_file_spec) + m_gdb_comm.SetSTDOUT(stdout_file_spec); + if (stderr_file_spec) + m_gdb_comm.SetSTDERR(stderr_file_spec); + + m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); + m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); + + m_gdb_comm.SendLaunchArchPacket( + GetTarget().GetArchitecture().GetArchitectureName()); + + const char *launch_event_data = launch_info.GetLaunchEventData(); + if (launch_event_data != nullptr && *launch_event_data != '\0') + m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); + + if (working_dir) { + m_gdb_comm.SetWorkingDir(working_dir); + } - { - // Scope for the scoped timeout object - GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, - std::chrono::seconds(10)); + // Send the environment and the program + arguments after we connect + m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); - int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); - if (arg_packet_err == 0) { - std::string error_str; - if (m_gdb_comm.GetLaunchSuccess(error_str)) { - SetID(m_gdb_comm.GetCurrentProcessID()); - } else { - error.SetErrorString(error_str.c_str()); - } + { + // Scope for the scoped timeout object + GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, + std::chrono::seconds(10)); + + int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); + if (arg_packet_err == 0) { + std::string error_str; + if (m_gdb_comm.GetLaunchSuccess(error_str)) { + SetID(m_gdb_comm.GetCurrentProcessID()); } else { - error.SetErrorStringWithFormat("'A' packet returned an error: %i", - arg_packet_err); + error.SetErrorString(error_str.c_str()); } + } else { + error.SetErrorStringWithFormat("'A' packet returned an error: %i", + arg_packet_err); } + } - if (GetID() == LLDB_INVALID_PROCESS_ID) { - LLDB_LOGF(log, "failed to connect to debugserver: %s", - error.AsCString()); - KillDebugserverProcess(); - return error; - } + if (GetID() == LLDB_INVALID_PROCESS_ID) { + LLDB_LOGF(log, "failed to connect to debugserver: %s", + error.AsCString()); + KillDebugserverProcess(); + return error; + } - StringExtractorGDBRemote response; - if (m_gdb_comm.GetStopReply(response)) { - SetLastStopPacket(response); - // '?' Packets must be handled differently in non-stop mode - if (GetTarget().GetNonStopModeEnabled()) - HandleStopReplySequence(); + StringExtractorGDBRemote response; + if (m_gdb_comm.GetStopReply(response)) { + SetLastStopPacket(response); - const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); + const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); - if (process_arch.IsValid()) { - GetTarget().MergeArchitecture(process_arch); - } else { - const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); - if (host_arch.IsValid()) - GetTarget().MergeArchitecture(host_arch); - } + if (process_arch.IsValid()) { + GetTarget().MergeArchitecture(process_arch); + } else { + const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); + if (host_arch.IsValid()) + GetTarget().MergeArchitecture(host_arch); + } - SetPrivateState(SetThreadStopInfo(response)); + SetPrivateState(SetThreadStopInfo(response)); - if (!disable_stdio) { - if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) - SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor()); - } + if (!disable_stdio) { + if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) + SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor()); } - } else { - LLDB_LOGF(log, "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()); + LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString()); } return error; } @@ -960,9 +827,6 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection(std::move(conn_up)); break; - } else if (error.WasInterrupted()) { - // If we were interrupted, don't keep retrying. - break; } retry_count++; @@ -981,11 +845,6 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { return error; } - // Start the communications read thread so all incoming data can be parsed - // into packets and queued as they arrive. - if (GetTarget().GetNonStopModeEnabled()) - m_gdb_comm.StartReadThread(); - // We always seem to be able to open a connection to a local port so we need // to make sure we can then send data to it. If we can't then we aren't // actually connected to anything, so try and do the handshake with the @@ -997,10 +856,6 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { return error; } - // Send $QNonStop:1 packet on startup if required - if (GetTarget().GetNonStopModeEnabled()) - GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true)); - m_gdb_comm.GetEchoSupported(); m_gdb_comm.GetThreadSuffixSupported(); m_gdb_comm.GetListThreadsInStopReplySupported(); @@ -1009,10 +864,6 @@ Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { m_gdb_comm.GetVAttachOrWaitSupported(); m_gdb_comm.EnableErrorStringInPacket(); - // Ask the remote server for the default thread id - if (GetTarget().GetNonStopModeEnabled()) - m_gdb_comm.GetDefaultThreadId(m_initial_tid); - size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; @@ -1113,6 +964,18 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { if (StructuredData::Array *supported_packets = m_gdb_comm.GetSupportedStructuredDataPlugins()) MapSupportedStructuredDataPlugins(*supported_packets); + + // If connected to LLDB ("native-signals+"), use signal defs for + // the remote platform. If connected to GDB, just use the standard set. + if (!m_gdb_comm.UsesNativeSignals()) { + SetUnixSignals(std::make_shared<GDBRemoteSignals>()); + } else { + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (platform_sp && platform_sp->IsConnected()) + SetUnixSignals(platform_sp->GetUnixSignals()); + else + SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); + } } void ProcessGDBRemote::MaybeLoadExecutableModule() { @@ -1272,10 +1135,9 @@ Status ProcessGDBRemote::DoResume() { StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport()) { - if (!GetTarget().GetNonStopModeEnabled() && - (m_continue_c_tids.size() == num_threads || - (m_continue_c_tids.empty() && m_continue_C_tids.empty() && - m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { + if (m_continue_c_tids.size() == num_threads || + (m_continue_c_tids.empty() && m_continue_C_tids.empty() && + m_continue_s_tids.empty() && m_continue_S_tids.empty())) { // All threads are continuing, just send a "c" packet continue_packet.PutCString("c"); } else { @@ -1393,14 +1255,7 @@ Status ProcessGDBRemote::DoResume() { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); - // If in Non-Stop-Mode use vCont when stepping - if (GetTarget().GetNonStopModeEnabled()) { - if (m_gdb_comm.GetVContSupported('s')) - continue_packet.PutCString("vCont;s"); - else - continue_packet.PutChar('s'); - } else - continue_packet.PutChar('s'); + continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && @@ -1514,21 +1369,14 @@ size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue( return m_thread_ids.size(); } -size_t -ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) { +size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue( + llvm::StringRef value) { m_thread_pcs.clear(); - size_t comma_pos; - lldb::addr_t pc; - while ((comma_pos = value.find(',')) != std::string::npos) { - value[comma_pos] = '\0'; - pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); - if (pc != LLDB_INVALID_ADDRESS) + for (llvm::StringRef x : llvm::split(value, ',')) { + lldb::addr_t pc; + if (llvm::to_integer(x, pc, 16)) m_thread_pcs.push_back(pc); - value.erase(0, comma_pos + 1); } - pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); - if (pc != LLDB_INVALID_ADDRESS) - m_thread_pcs.push_back(pc); return m_thread_pcs.size(); } @@ -1559,40 +1407,30 @@ bool ProcessGDBRemote::UpdateThreadIDList() { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair - // Lock the thread stack while we access it - // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); - std::unique_lock<std::recursive_mutex> stop_stack_lock( - m_last_stop_packet_mutex, std::defer_lock); - if (stop_stack_lock.try_lock()) { - // Get the number of stop packets on the stack - int nItems = m_stop_packet_stack.size(); - // Iterate over them - for (int i = 0; i < nItems; i++) { - // Get the thread stop info - StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; - const std::string &stop_info_str = - std::string(stop_info.GetStringRef()); + if (m_last_stop_packet) { + // Get the thread stop info + StringExtractorGDBRemote &stop_info = *m_last_stop_packet; + const std::string &stop_info_str = std::string(stop_info.GetStringRef()); - m_thread_pcs.clear(); - const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); - if (thread_pcs_pos != std::string::npos) { - const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); - const size_t end = stop_info_str.find(';', start); - if (end != std::string::npos) { - std::string value = stop_info_str.substr(start, end - start); - UpdateThreadPCsFromStopReplyThreadsValue(value); - } + m_thread_pcs.clear(); + const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); + if (thread_pcs_pos != std::string::npos) { + const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); + const size_t end = stop_info_str.find(';', start); + if (end != std::string::npos) { + std::string value = stop_info_str.substr(start, end - start); + UpdateThreadPCsFromStopReplyThreadsValue(value); } + } - const size_t threads_pos = stop_info_str.find(";threads:"); - if (threads_pos != std::string::npos) { - const size_t start = threads_pos + strlen(";threads:"); - const size_t end = stop_info_str.find(';', start); - if (end != std::string::npos) { - std::string value = stop_info_str.substr(start, end - start); - if (UpdateThreadIDsFromStopReplyThreadsValue(value)) - return true; - } + const size_t threads_pos = stop_info_str.find(";threads:"); + if (threads_pos != std::string::npos) { + const size_t start = threads_pos + strlen(";threads:"); + const size_t end = stop_info_str.find(';', start); + if (end != std::string::npos) { + std::string value = stop_info_str.substr(start, end - start); + if (UpdateThreadIDsFromStopReplyThreadsValue(value)) + return true; } } } @@ -1908,6 +1746,28 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( } else if (reason == "processor trace") { thread_sp->SetStopInfo(StopInfo::CreateStopReasonProcessorTrace( *thread_sp, description.c_str())); + } else if (reason == "fork") { + StringExtractor desc_extractor(description.c_str()); + lldb::pid_t child_pid = desc_extractor.GetU64( + LLDB_INVALID_PROCESS_ID); + lldb::tid_t child_tid = desc_extractor.GetU64( + LLDB_INVALID_THREAD_ID); + thread_sp->SetStopInfo(StopInfo::CreateStopReasonFork( + *thread_sp, child_pid, child_tid)); + handled = true; + } else if (reason == "vfork") { + StringExtractor desc_extractor(description.c_str()); + lldb::pid_t child_pid = desc_extractor.GetU64( + LLDB_INVALID_PROCESS_ID); + lldb::tid_t child_tid = desc_extractor.GetU64( + LLDB_INVALID_THREAD_ID); + thread_sp->SetStopInfo(StopInfo::CreateStopReasonVFork( + *thread_sp, child_pid, child_tid)); + handled = true; + } else if (reason == "vforkdone") { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonVForkDone(*thread_sp)); + handled = true; } } else if (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); @@ -2032,6 +1892,7 @@ ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { // Iterate through all of the thread dictionary key/value pairs from the // structured data dictionary + // FIXME: we're silently ignoring invalid data here thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, @@ -2096,9 +1957,8 @@ ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { registers_dict->ForEach( [&expedited_register_map](ConstString key, StructuredData::Object *object) -> bool { - const uint32_t reg = - StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10); - if (reg != UINT32_MAX) + uint32_t reg; + if (llvm::to_integer(key.AsCString(), reg)) expedited_register_map[reg] = std::string(object->GetStringValue()); return true; // Keep iterating through all array items @@ -2314,6 +2174,21 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG_ERROR(log, std::move(error), "Failed to load modules: {0}"); } + } else if (key.compare("fork") == 0 || key.compare("vfork") == 0) { + // fork includes child pid/tid in thread-id format + StringExtractorGDBRemote thread_id{value}; + auto pid_tid = thread_id.GetPidTid(LLDB_INVALID_PROCESS_ID); + if (!pid_tid) { + Log *log( + ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + LLDB_LOG(log, "Invalid PID/TID to fork: {0}", value); + pid_tid = {{LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID}}; + } + + reason = key.str(); + StreamString ostr; + ostr.Printf("%" PRIu64 " %" PRIu64, pid_tid->first, pid_tid->second); + description = std::string(ostr.GetString()); } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = UINT32_MAX; if (!key.getAsInteger(16, reg)) @@ -2380,22 +2255,9 @@ void ProcessGDBRemote::RefreshStateAfterStop() { // date before we do that or we might overwrite what was computed here. UpdateThreadListIfNeeded(); - // Scope for the lock - { - // Lock the thread stack while we access it - std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); - // Get the number of stop packets on the stack - int nItems = m_stop_packet_stack.size(); - // Iterate over them - for (int i = 0; i < nItems; i++) { - // Get the thread stop info - StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; - // Process thread stop info - SetThreadStopInfo(stop_info); - } - // Clear the thread stop stack - m_stop_packet_stack.clear(); - } + if (m_last_stop_packet) + SetThreadStopInfo(*m_last_stop_packet); + m_last_stop_packet.reset(); // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { @@ -2479,9 +2341,9 @@ Status ProcessGDBRemote::DoDestroy() { 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()) { + platform_sp->GetName().GetStringRef() == + PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to " @@ -2644,20 +2506,7 @@ void ProcessGDBRemote::SetLastStopPacket( m_gdb_comm.ResetDiscoverableSettings(did_exec); } - // Scope the lock - { - // Lock the thread stack while we access it - std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); - - // We are are not using non-stop mode, there can only be one last stop - // reply packet, so clear the list. - if (!GetTarget().GetNonStopModeEnabled()) - m_stop_packet_stack.clear(); - - // Add this stop packet to the stop packet stack This stack will get popped - // and examined when we switch to the Stopped state - m_stop_packet_stack.push_back(response); - } + m_last_stop_packet = response; } void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { @@ -3052,8 +2901,8 @@ lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, return allocated_addr; } -Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr, - MemoryRegionInfo ®ion_info) { +Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, + MemoryRegionInfo ®ion_info) { Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); return error; @@ -3638,7 +3487,7 @@ void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( - debugger, GetGlobalPluginProperties()->GetValueProperties(), + debugger, GetGlobalPluginProperties().GetValueProperties(), ConstString("Properties for the gdb-remote process plug-in."), is_global_setting); } @@ -3774,88 +3623,72 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; - // If in Non-Stop-Mode - if (process->GetTarget().GetNonStopModeEnabled()) { - // send the vCont packet - if (!process->GetGDBRemote().SendvContPacket( - llvm::StringRef(continue_cstr, continue_cstr_len), - process->GetInterruptTimeout(), response)) { - // Something went wrong - done = true; - break; - } - } - // If in All-Stop-Mode - else { - StateType stop_state = - process->GetGDBRemote().SendContinuePacketAndWaitForResponse( - *process, *process->GetUnixSignals(), - llvm::StringRef(continue_cstr, continue_cstr_len), - process->GetInterruptTimeout(), - 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(); + StateType stop_state = + process->GetGDBRemote().SendContinuePacketAndWaitForResponse( + *process, *process->GetUnixSignals(), + llvm::StringRef(continue_cstr, continue_cstr_len), + process->GetInterruptTimeout(), 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; - 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); - - int exit_status = response.GetHexU8(); - std::string desc_string; - if (response.GetBytesLeft() > 0 && - response.GetChar('-') == ';') { - llvm::StringRef desc_str; - llvm::StringRef desc_token; - while (response.GetNameColonValue(desc_token, desc_str)) { - if (desc_token != "description") - continue; - StringExtractor extractor(desc_str); - extractor.GetHexByteString(desc_string); - } + case eStateExited: { + process->SetLastStopPacket(response); + process->ClearThreadIDList(); + response.SetFilePos(1); + + int exit_status = response.GetHexU8(); + std::string desc_string; + if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { + llvm::StringRef desc_str; + llvm::StringRef desc_token; + while (response.GetNameColonValue(desc_token, desc_str)) { + if (desc_token != "description") + continue; + StringExtractor extractor(desc_str); + extractor.GetHexByteString(desc_string); } - process->SetExitStatus(exit_status, desc_string.c_str()); - done = true; - break; } - case eStateInvalid: { - // Check to see if we were trying to attach and if we got back - // the "E87" error code from debugserver -- this indicates that - // the process is not debuggable. Return a slightly more - // helpful error message about why the attach failed. - if (::strstr(continue_cstr, "vAttach") != nullptr && - response.GetError() == 0x87) { - process->SetExitStatus(-1, "cannot attach to process due to " - "System Integrity Protection"); - } else if (::strstr(continue_cstr, "vAttach") != nullptr && - response.GetStatus().Fail()) { - process->SetExitStatus(-1, response.GetStatus().AsCString()); - } else { - process->SetExitStatus(-1, "lost connection"); - } - done = true; - break; + process->SetExitStatus(exit_status, desc_string.c_str()); + done = true; + break; + } + case eStateInvalid: { + // Check to see if we were trying to attach and if we got back + // the "E87" error code from debugserver -- this indicates that + // the process is not debuggable. Return a slightly more + // helpful error message about why the attach failed. + if (::strstr(continue_cstr, "vAttach") != nullptr && + response.GetError() == 0x87) { + process->SetExitStatus(-1, "cannot attach to process due to " + "System Integrity Protection"); + } else if (::strstr(continue_cstr, "vAttach") != nullptr && + response.GetStatus().Fail()) { + process->SetExitStatus(-1, response.GetStatus().AsCString()); + } else { + process->SetExitStatus(-1, "lost connection"); } + done = true; + break; + } - default: - process->SetPrivateState(stop_state); - break; - } // switch(stop_state) - } // else // if in All-stop-mode + default: + process->SetPrivateState(stop_state); + break; + } // switch(stop_state) } // if (continue_packet) } // case eBroadcastBitAsyncContinue break; @@ -4027,7 +3860,7 @@ bool ProcessGDBRemote::StopNoticingNewThreads() { DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { if (m_dyld_up.get() == nullptr) - m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr)); + m_dyld_up.reset(DynamicLoader::FindPlugin(this, "")); return m_dyld_up.get(); } @@ -4051,12 +3884,14 @@ Status ProcessGDBRemote::SendEventData(const char *data) { DataExtractor ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { - std::string response_string; - if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", - response_string) == - GDBRemoteCommunication::PacketResult::Success) - buf = std::make_shared<DataBufferHeap>(response_string.c_str(), - response_string.length()); + llvm::Expected<std::string> response = m_gdb_comm.ReadExtFeature("auxv", ""); + if (response) + buf = std::make_shared<DataBufferHeap>(response->c_str(), + response->length()); + else + LLDB_LOG_ERROR( + ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS), + response.takeError(), "{0}"); } return DataExtractor(buf, GetByteOrder(), GetAddressByteSize()); } @@ -4351,132 +4186,84 @@ struct GdbServerTargetInfo { }; bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, - GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, - uint32_t ®_num_remote, uint32_t ®_num_local) { + std::vector<DynamicRegisterInfo::Register> ®isters) { if (!feature_node) return false; - uint32_t reg_offset = LLDB_INVALID_INDEX32; feature_node.ForEachChildElementWithName( - "reg", [&target_info, &dyn_reg_info, ®_num_remote, ®_num_local, - ®_offset, &abi_sp](const XMLNode ®_node) -> bool { + "reg", [&target_info, ®isters](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; - ConstString reg_name; - ConstString alt_name; - ConstString set_name; - std::vector<uint32_t> value_regs; - std::vector<uint32_t> invalidate_regs; - std::vector<uint8_t> dwarf_opcode_bytes; + DynamicRegisterInfo::Register reg_info; bool encoding_set = false; bool format_set = false; - RegisterInfo reg_info = { - nullptr, // Name - nullptr, // Alt name - 0, // byte size - reg_offset, // offset - eEncodingUint, // encoding - eFormatHex, // format - { - LLDB_INVALID_REGNUM, // eh_frame reg num - LLDB_INVALID_REGNUM, // DWARF reg num - LLDB_INVALID_REGNUM, // generic reg num - reg_num_remote, // process plugin reg num - reg_num_local // native register number - }, - nullptr, - nullptr, - nullptr, // Dwarf Expression opcode bytes pointer - 0 // Dwarf Expression opcode bytes length - }; + // FIXME: we're silently ignoring invalid data here reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, - ®_name, &alt_name, &set_name, &value_regs, - &invalidate_regs, &encoding_set, &format_set, - ®_info, ®_offset, &dwarf_opcode_bytes]( + &encoding_set, &format_set, ®_info]( const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { - reg_name.SetString(value); + reg_info.name.SetString(value); } else if (name == "bitsize") { - reg_info.byte_size = - StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; + if (llvm::to_integer(value, reg_info.byte_size)) + reg_info.byte_size = + llvm::divideCeil(reg_info.byte_size, CHAR_BIT); } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { - const uint32_t regnum = - StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); - if (regnum != LLDB_INVALID_REGNUM) { - reg_info.kinds[eRegisterKindProcessPlugin] = regnum; - } + llvm::to_integer(value, reg_info.regnum_remote); } else if (name == "offset") { - reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); + llvm::to_integer(value, reg_info.byte_offset); } else if (name == "altname") { - alt_name.SetString(value); + reg_info.alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); } else if (name == "format") { format_set = true; - Format format = eFormatInvalid; - if (OptionArgParser::ToFormat(value.data(), format, nullptr) - .Success()) - reg_info.format = format; - else if (value == "vector-sint8") - reg_info.format = eFormatVectorOfSInt8; - else if (value == "vector-uint8") - reg_info.format = eFormatVectorOfUInt8; - else if (value == "vector-sint16") - reg_info.format = eFormatVectorOfSInt16; - else if (value == "vector-uint16") - reg_info.format = eFormatVectorOfUInt16; - else if (value == "vector-sint32") - reg_info.format = eFormatVectorOfSInt32; - else if (value == "vector-uint32") - reg_info.format = eFormatVectorOfUInt32; - else if (value == "vector-float32") - reg_info.format = eFormatVectorOfFloat32; - else if (value == "vector-uint64") - reg_info.format = eFormatVectorOfUInt64; - else if (value == "vector-uint128") - reg_info.format = eFormatVectorOfUInt128; + if (!OptionArgParser::ToFormat(value.data(), reg_info.format, + nullptr) + .Success()) + reg_info.format = + llvm::StringSwitch<lldb::Format>(value) + .Case("vector-sint8", eFormatVectorOfSInt8) + .Case("vector-uint8", eFormatVectorOfUInt8) + .Case("vector-sint16", eFormatVectorOfSInt16) + .Case("vector-uint16", eFormatVectorOfUInt16) + .Case("vector-sint32", eFormatVectorOfSInt32) + .Case("vector-uint32", eFormatVectorOfUInt32) + .Case("vector-float32", eFormatVectorOfFloat32) + .Case("vector-uint64", eFormatVectorOfUInt64) + .Case("vector-uint128", eFormatVectorOfUInt128) + .Default(eFormatInvalid); } else if (name == "group_id") { - const uint32_t set_id = - StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); + uint32_t set_id = UINT32_MAX; + llvm::to_integer(value, set_id); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) - set_name = pos->second.name; + reg_info.set_name = pos->second.name; } else if (name == "gcc_regnum" || name == "ehframe_regnum") { - reg_info.kinds[eRegisterKindEHFrame] = - StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); + llvm::to_integer(value, reg_info.regnum_ehframe); } else if (name == "dwarf_regnum") { - reg_info.kinds[eRegisterKindDWARF] = - StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); + llvm::to_integer(value, reg_info.regnum_dwarf); } else if (name == "generic") { - reg_info.kinds[eRegisterKindGeneric] = - Args::StringToGenericRegister(value); + reg_info.regnum_generic = Args::StringToGenericRegister(value); } else if (name == "value_regnums") { - SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); + SplitCommaSeparatedRegisterNumberString(value, reg_info.value_regs, + 0); } else if (name == "invalidate_regnums") { - SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); - } else if (name == "dynamic_size_dwarf_expr_bytes") { - std::string opcode_string = value.str(); - size_t dwarf_opcode_len = opcode_string.length() / 2; - assert(dwarf_opcode_len > 0); - - dwarf_opcode_bytes.resize(dwarf_opcode_len); - reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; - StringExtractor opcode_extractor(opcode_string); - uint32_t ret_val = - opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); - assert(dwarf_opcode_len == ret_val); - UNUSED_IF_ASSERT_DISABLED(ret_val); - reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); + SplitCommaSeparatedRegisterNumberString( + value, reg_info.invalidate_regs, 0); } else { - printf("unhandled attribute %s = %s\n", name.data(), value.data()); + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet( + GDBR_LOG_PROCESS)); + LLDB_LOGF(log, + "ProcessGDBRemote::%s unhandled reg attribute %s = %s", + __FUNCTION__, name.data(), value.data()); } return true; // Keep iterating through all attributes }); @@ -4488,43 +4275,40 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; - } else if (gdb_type == "i387_ext" || gdb_type == "float") { + } else if (gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; + } else if (gdb_type == "aarch64v" || + llvm::StringRef(gdb_type).startswith("vec") || + gdb_type == "i387_ext" || gdb_type == "uint128") { + // lldb doesn't handle 128-bit uints correctly (for ymm*h), so treat + // them as vector (similarly to xmm/ymm) + reg_info.format = eFormatVectorOfUInt8; + reg_info.encoding = eEncodingVector; } } // Only update the register set name if we didn't get a "reg_set" // attribute. "set_name" will be empty if we didn't have a "reg_set" // attribute. - if (!set_name) { + if (!reg_info.set_name) { if (!gdb_group.empty()) { - set_name.SetCString(gdb_group.c_str()); + reg_info.set_name.SetCString(gdb_group.c_str()); } else { // If no register group name provided anywhere, // we'll create a 'general' register set - set_name.SetCString("general"); + reg_info.set_name.SetCString("general"); } } - reg_info.byte_offset = reg_offset; - assert(reg_info.byte_size != 0); - reg_offset = LLDB_INVALID_INDEX32; - 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(); - } - - reg_num_remote = reg_info.kinds[eRegisterKindProcessPlugin] + 1; - ++reg_num_local; - reg_info.name = reg_name.AsCString(); - if (abi_sp) - abi_sp->AugmentRegisterInfo(reg_info); - dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); + if (reg_info.byte_size == 0) { + Log *log( + ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + LLDB_LOGF(log, + "ProcessGDBRemote::%s Skipping zero bitsize register %s", + __FUNCTION__, reg_info.name.AsCString()); + } else + registers.push_back(reg_info); return true; // Keep iterating through all "reg" elements }); @@ -4539,20 +4323,17 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, // for nested register definition files. It returns true if it was able // to fetch and parse an xml file. bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( - ArchSpec &arch_to_use, std::string xml_filename, uint32_t ®_num_remote, - uint32_t ®_num_local) { + ArchSpec &arch_to_use, std::string xml_filename, + std::vector<DynamicRegisterInfo::Register> ®isters) { // request the target xml file - std::string raw; - lldb_private::Status lldberr; - if (!m_gdb_comm.ReadExtFeature(ConstString("features"), - ConstString(xml_filename.c_str()), raw, - lldberr)) { + llvm::Expected<std::string> raw = m_gdb_comm.ReadExtFeature("features", xml_filename); + if (errorToBool(raw.takeError())) return false; - } XMLDocument xml_document; - if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) { + if (xml_document.ParseMemory(raw->c_str(), raw->size(), + xml_filename.c_str())) { GdbServerTargetInfo target_info; std::vector<XMLNode> feature_nodes; @@ -4581,9 +4362,9 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( node.ForEachAttribute( [&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { + // FIXME: we're silently ignoring invalid data here if (name == "id") - set_id = StringConvert::ToUInt32(value.data(), - UINT32_MAX, 0); + llvm::to_integer(value, set_id); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes @@ -4617,39 +4398,33 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( } } - // If the target.xml includes an architecture entry like + // gdbserver does not implement the LLDB packets used to determine host + // or process architecture. If that is the case, attempt to use + // the <architecture/> field from target.xml, e.g.: + // // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi) - // <architecture>arm</architecture> (seen from Segger JLink on unspecified arm board) - // use that if we don't have anything better. + // <architecture>arm</architecture> (seen from Segger JLink on unspecified + // arm board) if (!arch_to_use.IsValid() && !target_info.arch.empty()) { - if (target_info.arch == "i386:x86-64") { - // We don't have any information about vendor or OS. - arch_to_use.SetTriple("x86_64--"); - GetTarget().MergeArchitecture(arch_to_use); - } + // We don't have any information about vendor or OS. + arch_to_use.SetTriple(llvm::StringSwitch<std::string>(target_info.arch) + .Case("i386:x86-64", "x86_64") + .Default(target_info.arch) + + "--"); - // SEGGER J-Link jtag boards send this very-generic arch name, - // we'll need to use this if we have absolutely nothing better - // to work with or the register definitions won't be accepted. - if (target_info.arch == "arm") { - arch_to_use.SetTriple("arm--"); + if (arch_to_use.IsValid()) GetTarget().MergeArchitecture(arch_to_use); - } } if (arch_to_use.IsValid()) { - // Don't use Process::GetABI, this code gets called from DidAttach, and - // in that context we haven't set the Target's architecture yet, so the - // ABI is also potentially incorrect. - ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use); for (auto &feature_node : feature_nodes) { - ParseRegisters(feature_node, target_info, *this->m_register_info_sp, - abi_to_use_sp, reg_num_remote, reg_num_local); + ParseRegisters(feature_node, target_info, + registers); } for (const auto &include : target_info.includes) { GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include, - reg_num_remote, reg_num_local); + registers); } } } else { @@ -4658,6 +4433,46 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( return true; } +void ProcessGDBRemote::AddRemoteRegisters( + std::vector<DynamicRegisterInfo::Register> ®isters, + const ArchSpec &arch_to_use) { + std::map<uint32_t, uint32_t> remote_to_local_map; + uint32_t remote_regnum = 0; + for (auto it : llvm::enumerate(registers)) { + DynamicRegisterInfo::Register &remote_reg_info = it.value(); + + // Assign successive remote regnums if missing. + if (remote_reg_info.regnum_remote == LLDB_INVALID_REGNUM) + remote_reg_info.regnum_remote = remote_regnum; + + // Create a mapping from remote to local regnos. + remote_to_local_map[remote_reg_info.regnum_remote] = it.index(); + + remote_regnum = remote_reg_info.regnum_remote + 1; + } + + for (DynamicRegisterInfo::Register &remote_reg_info : registers) { + auto proc_to_lldb = [&remote_to_local_map](uint32_t process_regnum) { + auto lldb_regit = remote_to_local_map.find(process_regnum); + return lldb_regit != remote_to_local_map.end() ? lldb_regit->second + : LLDB_INVALID_REGNUM; + }; + + llvm::transform(remote_reg_info.value_regs, + remote_reg_info.value_regs.begin(), proc_to_lldb); + llvm::transform(remote_reg_info.invalidate_regs, + remote_reg_info.invalidate_regs.begin(), proc_to_lldb); + } + + // Don't use Process::GetABI, this code gets called from DidAttach, and + // in that context we haven't set the Target's architecture yet, so the + // ABI is also potentially incorrect. + if (ABISP abi_sp = ABI::FindPlugin(shared_from_this(), arch_to_use)) + abi_sp->AugmentRegisterInfo(registers); + + m_register_info_sp->SetRegisterInfo(std::move(registers), arch_to_use); +} + // query the target of gdb-remote for extended target information returns // true on success (got register definitions), false on failure (did not). bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { @@ -4669,11 +4484,10 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { if (!m_gdb_comm.GetQXferFeaturesReadSupported()) return false; - uint32_t reg_num_remote = 0; - uint32_t reg_num_local = 0; + std::vector<DynamicRegisterInfo::Register> registers; if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml", - reg_num_remote, reg_num_local)) - this->m_register_info_sp->Finalize(arch_to_use); + registers)) + AddRemoteRegisters(registers, arch_to_use); return m_register_info_sp->GetNumRegisters() > 0; } @@ -4689,24 +4503,20 @@ llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { LoadedModuleInfoList list; GDBRemoteCommunicationClient &comm = m_gdb_comm; - bool can_use_svr4 = GetGlobalPluginProperties()->GetUseSVR4(); + bool can_use_svr4 = GetGlobalPluginProperties().GetUseSVR4(); // check that we have extended feature read support if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) { // request the loaded library list - std::string raw; - lldb_private::Status lldberr; - - if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""), - raw, lldberr)) - return llvm::createStringError(llvm::inconvertibleErrorCode(), - "Error in libraries-svr4 packet"); + llvm::Expected<std::string> raw = comm.ReadExtFeature("libraries-svr4", ""); + if (!raw) + return raw.takeError(); // parse the xml file in memory - LLDB_LOGF(log, "parsing: %s", raw.c_str()); + LLDB_LOGF(log, "parsing: %s", raw->c_str()); XMLDocument doc; - if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) + if (!doc.ParseMemory(raw->c_str(), raw->size(), "noname.xml")) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error reading noname.xml"); @@ -4718,38 +4528,37 @@ llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); - if (!main_lm.empty()) { - list.m_link_map = - StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); - } + // FIXME: we're silently ignoring invalid data here + if (!main_lm.empty()) + llvm::to_integer(main_lm, list.m_link_map); root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { - LoadedModuleInfoList::LoadedModuleInfo module; + // FIXME: we're silently ignoring invalid data here library.ForEachAttribute( [&module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { - + uint64_t uint_value = LLDB_INVALID_ADDRESS; if (name == "name") module.set_name(value.str()); else if (name == "lm") { // the address of the link_map struct. - module.set_link_map(StringConvert::ToUInt64( - value.data(), LLDB_INVALID_ADDRESS, 0)); + llvm::to_integer(value, uint_value); + module.set_link_map(uint_value); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the // link_map struct. - module.set_base(StringConvert::ToUInt64( - value.data(), LLDB_INVALID_ADDRESS, 0)); + llvm::to_integer(value, uint_value); + module.set_base(uint_value); // base address is always a displacement, not an absolute // value. module.set_base_is_offset(true); } else if (name == "l_ld") { // the memory address of the libraries PT_DYNAMIC section. - module.set_dynamic(StringConvert::ToUInt64( - value.data(), LLDB_INVALID_ADDRESS, 0)); + llvm::to_integer(value, uint_value); + module.set_dynamic(uint_value); } return true; // Keep iterating over all properties of "library" @@ -4784,18 +4593,15 @@ llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { return list; } else if (comm.GetQXferLibrariesReadSupported()) { // request the loaded library list - std::string raw; - lldb_private::Status lldberr; + llvm::Expected<std::string> raw = comm.ReadExtFeature("libraries", ""); - if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw, - lldberr)) - return llvm::createStringError(llvm::inconvertibleErrorCode(), - "Error in libraries packet"); + if (!raw) + return raw.takeError(); - LLDB_LOGF(log, "parsing: %s", raw.c_str()); + LLDB_LOGF(log, "parsing: %s", raw->c_str()); XMLDocument doc; - if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) + if (!doc.ParseMemory(raw->c_str(), raw->size(), "noname.xml")) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error reading noname.xml"); @@ -4804,6 +4610,7 @@ llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error finding library-list xml element"); + // FIXME: we're silently ignoring invalid data here root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; @@ -4817,8 +4624,9 @@ llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); - module.set_base( - StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); + uint64_t address_value = LLDB_INVALID_ADDRESS; + llvm::to_integer(address, address_value); + module.set_base(address_value); // These addresses are absolute values. module.set_base_is_offset(false); @@ -5113,6 +4921,56 @@ void ProcessGDBRemote::HandleStopReply() { BuildDynamicRegisterInfo(true); } +llvm::Expected<bool> ProcessGDBRemote::SaveCore(llvm::StringRef outfile) { + if (!m_gdb_comm.GetSaveCoreSupported()) + return false; + + StreamString packet; + packet.PutCString("qSaveCore;path-hint:"); + packet.PutStringAsRawHex8(outfile); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success) { + // TODO: grab error message from the packet? StringExtractor seems to + // be missing a method for that + if (response.IsErrorResponse()) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + llvm::formatv("qSaveCore returned an error")); + + std::string path; + + // process the response + for (auto x : llvm::split(response.GetStringRef(), ';')) { + if (x.consume_front("core-path:")) + StringExtractor(x).GetHexByteString(path); + } + + // verify that we've gotten what we need + if (path.empty()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "qSaveCore returned no core path"); + + // now transfer the core file + FileSpec remote_core{llvm::StringRef(path)}; + Platform &platform = *GetTarget().GetPlatform(); + Status error = platform.GetFile(remote_core, FileSpec(outfile)); + + if (platform.IsRemote()) { + // NB: we unlink the file on error too + platform.Unlink(remote_core); + if (error.Fail()) + return error.ToError(); + } + + return true; + } + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to send qSaveCore"); +} + static const char *const s_async_json_packet_prefix = "JSON-async:"; static StructuredData::ObjectSP @@ -5451,3 +5309,171 @@ CommandObject *ProcessGDBRemote::GetPluginCommandObject() { GetTarget().GetDebugger().GetCommandInterpreter()); return m_command_sp.get(); } + +void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(bool enable) { + GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) { + if (bp_site->IsEnabled() && + (bp_site->GetType() == BreakpointSite::eSoftware || + bp_site->GetType() == BreakpointSite::eExternal)) { + m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointSoftware, enable, bp_site->GetLoadAddress(), + GetSoftwareBreakpointTrapOpcode(bp_site), GetInterruptTimeout()); + } + }); +} + +void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) { + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { + GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) { + if (bp_site->IsEnabled() && + bp_site->GetType() == BreakpointSite::eHardware) { + m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointHardware, enable, bp_site->GetLoadAddress(), + GetSoftwareBreakpointTrapOpcode(bp_site), GetInterruptTimeout()); + } + }); + } + + WatchpointList &wps = GetTarget().GetWatchpointList(); + size_t wp_count = wps.GetSize(); + for (size_t i = 0; i < wp_count; ++i) { + WatchpointSP wp = wps.GetByIndex(i); + if (wp->IsEnabled()) { + GDBStoppointType type = GetGDBStoppointType(wp.get()); + m_gdb_comm.SendGDBStoppointTypePacket(type, enable, wp->GetLoadAddress(), + wp->GetByteSize(), + GetInterruptTimeout()); + } + } +} + +void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + lldb::pid_t parent_pid = m_gdb_comm.GetCurrentProcessID(); + // Any valid TID will suffice, thread-relevant actions will set a proper TID + // anyway. + lldb::tid_t parent_tid = m_thread_ids.front(); + + lldb::pid_t follow_pid, detach_pid; + lldb::tid_t follow_tid, detach_tid; + + switch (GetFollowForkMode()) { + case eFollowParent: + follow_pid = parent_pid; + follow_tid = parent_tid; + detach_pid = child_pid; + detach_tid = child_tid; + break; + case eFollowChild: + follow_pid = child_pid; + follow_tid = child_tid; + detach_pid = parent_pid; + detach_tid = parent_tid; + break; + } + + // Switch to the process that is going to be detached. + if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid"); + return; + } + + // Disable all software breakpoints in the forked process. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(false); + + // Remove hardware breakpoints / watchpoints from parent process if we're + // following child. + if (GetFollowForkMode() == eFollowChild) + DidForkSwitchHardwareTraps(false); + + // Switch to the process that is going to be followed + if (!m_gdb_comm.SetCurrentThread(follow_tid, follow_pid) || + !m_gdb_comm.SetCurrentThreadForRun(follow_tid, follow_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid"); + return; + } + + LLDB_LOG(log, "Detaching process {0}", detach_pid); + Status error = m_gdb_comm.Detach(false, detach_pid); + if (error.Fail()) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() detach packet send failed: {0}", + error.AsCString() ? error.AsCString() : "<unknown error>"); + return; + } + + // Hardware breakpoints/watchpoints are not inherited implicitly, + // so we need to readd them if we're following child. + if (GetFollowForkMode() == eFollowChild) + DidForkSwitchHardwareTraps(true); +} + +void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + assert(!m_vfork_in_progress); + m_vfork_in_progress = true; + + // Disable all software breakpoints for the duration of vfork. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(false); + + lldb::pid_t detach_pid; + lldb::tid_t detach_tid; + + switch (GetFollowForkMode()) { + case eFollowParent: + detach_pid = child_pid; + detach_tid = child_tid; + break; + case eFollowChild: + detach_pid = m_gdb_comm.GetCurrentProcessID(); + // Any valid TID will suffice, thread-relevant actions will set a proper TID + // anyway. + detach_tid = m_thread_ids.front(); + + // Switch to the parent process before detaching it. + if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid"); + return; + } + + // Remove hardware breakpoints / watchpoints from the parent process. + DidForkSwitchHardwareTraps(false); + + // Switch to the child process. + if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid) || + !m_gdb_comm.SetCurrentThreadForRun(child_tid, child_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid"); + return; + } + break; + } + + LLDB_LOG(log, "Detaching process {0}", detach_pid); + Status error = m_gdb_comm.Detach(false, detach_pid); + if (error.Fail()) { + LLDB_LOG(log, + "ProcessGDBRemote::DidFork() detach packet send failed: {0}", + error.AsCString() ? error.AsCString() : "<unknown error>"); + return; + } +} + +void ProcessGDBRemote::DidVForkDone() { + assert(m_vfork_in_progress); + m_vfork_in_progress = false; + + // Reenable all software breakpoints that were enabled before vfork. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(true); +} + +void ProcessGDBRemote::DidExec() { + // If we are following children, vfork is finished by exec (rather than + // vforkdone that is submitted for parent). + if (GetFollowForkMode() == eFollowChild) + m_vfork_in_progress = false; + Process::DidExec(); +} |