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 | 338 |
1 files changed, 260 insertions, 78 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 eb42b9eb6cb6..604c92369e9a 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 @@ -24,6 +24,7 @@ #include <sys/types.h> #include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointAlgorithms.h" #include "lldb/Breakpoint/WatchpointResource.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" @@ -262,10 +263,9 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), m_max_memory_size(0), m_remote_stub_max_memory_size(0), m_addr_to_mmap_size(), m_thread_create_bp_sp(), - m_waiting_for_attach(false), - m_command_sp(), m_breakpoint_pc_offset(0), + m_waiting_for_attach(false), m_command_sp(), m_breakpoint_pc_offset(0), m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false), - m_erased_flash_ranges(), m_vfork_in_progress(false) { + m_erased_flash_ranges(), m_vfork_in_progress_count(0) { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, @@ -448,20 +448,20 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { DynamicRegisterInfo::Register reg_info; while (response.GetNameColonValue(name, value)) { - if (name.equals("name")) { + if (name == "name") { reg_info.name.SetString(value); - } else if (name.equals("alt-name")) { + } else if (name == "alt-name") { reg_info.alt_name.SetString(value); - } else if (name.equals("bitsize")) { + } else if (name == "bitsize") { if (!value.getAsInteger(0, reg_info.byte_size)) reg_info.byte_size /= CHAR_BIT; - } else if (name.equals("offset")) { + } else if (name == "offset") { value.getAsInteger(0, reg_info.byte_offset); - } else if (name.equals("encoding")) { + } else if (name == "encoding") { const Encoding encoding = Args::StringToEncoding(value); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; - } else if (name.equals("format")) { + } else if (name == "format") { if (!OptionArgParser::ToFormat(value.str().c_str(), reg_info.format, nullptr) .Success()) reg_info.format = @@ -480,17 +480,17 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { .Case("vector-uint64", eFormatVectorOfUInt64) .Case("vector-uint128", eFormatVectorOfUInt128) .Default(eFormatInvalid); - } else if (name.equals("set")) { + } else if (name == "set") { reg_info.set_name.SetString(value); - } else if (name.equals("gcc") || name.equals("ehframe")) { + } else if (name == "gcc" || name == "ehframe") { value.getAsInteger(0, reg_info.regnum_ehframe); - } else if (name.equals("dwarf")) { + } else if (name == "dwarf") { value.getAsInteger(0, reg_info.regnum_dwarf); - } else if (name.equals("generic")) { + } else if (name == "generic") { reg_info.regnum_generic = Args::StringToGenericRegister(value); - } else if (name.equals("container-regs")) { + } else if (name == "container-regs") { SplitCommaSeparatedRegisterNumberString(value, reg_info.value_regs, 16); - } else if (name.equals("invalidate-regs")) { + } else if (name == "invalidate-regs") { SplitCommaSeparatedRegisterNumberString(value, reg_info.invalidate_regs, 16); } } @@ -900,7 +900,7 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { } AddressableBits addressable_bits = m_gdb_comm.GetAddressableBits(); - addressable_bits.SetProcessMasks(*this); + SetAddressableBitMasks(addressable_bits); if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); @@ -1089,7 +1089,8 @@ Status ProcessGDBRemote::DoAttachToProcessWithID( const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID(attach_pid); - auto data_sp = std::make_shared<EventDataBytes>(packet, packet_len); + auto data_sp = + std::make_shared<EventDataBytes>(llvm::StringRef(packet, packet_len)); m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); } else SetExitStatus(-1, error.AsCString()); @@ -1127,8 +1128,7 @@ Status ProcessGDBRemote::DoAttachToProcessWithName( endian::InlHostByteOrder(), endian::InlHostByteOrder()); - auto data_sp = std::make_shared<EventDataBytes>(packet.GetString().data(), - packet.GetSize()); + auto data_sp = std::make_shared<EventDataBytes>(packet.GetString()); m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); } else @@ -1374,8 +1374,8 @@ Status ProcessGDBRemote::DoResume() { return error; } - auto data_sp = std::make_shared<EventDataBytes>( - continue_packet.GetString().data(), continue_packet.GetSize()); + auto data_sp = + std::make_shared<EventDataBytes>(continue_packet.GetString()); m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) { @@ -1599,6 +1599,26 @@ bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { // has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. + const size_t num_thread_ids = m_thread_ids.size(); + for (size_t i = 0; i < num_thread_ids; i++) { + if (m_thread_ids[i] == thread->GetID() && m_thread_pcs.size() > i) { + addr_t pc = m_thread_pcs[i]; + lldb::BreakpointSiteSP bp_site_sp = + thread->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) { + if (bp_site_sp->ValidForThisThread(*thread)) { + thread->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread, bp_site_sp->GetID())); + return true; + } + } + } + } thread->SetStopInfo(StopInfoSP()); } return true; @@ -1721,7 +1741,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( } else { bool handled = false; bool did_exec = false; - if (!reason.empty()) { + // debugserver can send reason = "none" which is equivalent + // to no reason. + if (!reason.empty() && reason != "none") { if (reason == "trace") { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = @@ -1863,11 +1885,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); - // If the current pc is a breakpoint site then the StopInfo should be - // set to Breakpoint even though the remote stub did not set it as such. - // This can happen when the thread is involuntarily interrupted (e.g. - // due to stops on other threads) just as it is about to execute the - // breakpoint instruction. + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. if (bp_site_sp && bp_site_sp->ValidForThisThread(*thread_sp)) { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); @@ -2316,7 +2337,7 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { } } - addressable_bits.SetProcessMasks(*this); + SetAddressableBitMasks(addressable_bits); ThreadSP thread_sp = SetThreadStopInfo( tid, expedited_register_map, signo, thread_name, reason, description, @@ -3153,23 +3174,13 @@ Status ProcessGDBRemote::EnableWatchpoint(WatchpointSP wp_sp, bool notify) { bool write = wp_sp->WatchpointWrite() || wp_sp->WatchpointModify(); size_t size = wp_sp->GetByteSize(); - // New WatchpointResources needed to implement this Watchpoint. - std::vector<WatchpointResourceSP> resources; - - // LWP_TODO: Break up the user's request into pieces that can be watched - // given the capabilities of the target cpu / stub software. - // As a default, breaking the watched region up into target-pointer-sized, - // aligned, groups. - // - // Beyond the default, a stub can / should inform us of its capabilities, - // e.g. a stub that can do AArch64 power-of-2 MASK watchpoints. - // - // And the cpu may have unique capabilities. AArch64 BAS watchpoints - // can watch any sequential bytes in a doubleword, but Intel watchpoints - // can only watch 1, 2, 4, 8 bytes within a doubleword. - WatchpointResourceSP wp_res_sp = - std::make_shared<WatchpointResource>(addr, size, read, write); - resources.push_back(wp_res_sp); + ArchSpec target_arch = GetTarget().GetArchitecture(); + WatchpointHardwareFeature supported_features = + m_gdb_comm.GetSupportedWatchpointTypes(); + + std::vector<WatchpointResourceSP> resources = + WatchpointAlgorithms::AtomizeWatchpointRequest( + addr, size, read, write, supported_features, target_arch); // LWP_TODO: Now that we know the WP Resources needed to implement this // Watchpoint, we need to look at currently allocated Resources in the @@ -4168,21 +4179,134 @@ struct GdbServerTargetInfo { RegisterSetMap reg_set_map; }; -static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, - unsigned size) { +static FieldEnum::Enumerators ParseEnumEvalues(const XMLNode &enum_node) { + Log *log(GetLog(GDBRLog::Process)); + // We will use the last instance of each value. Also we preserve the order + // of declaration in the XML, as it may not be numerical. + // For example, hardware may intially release with two states that softwware + // can read from a register field: + // 0 = startup, 1 = running + // If in a future hardware release, the designers added a pre-startup state: + // 0 = startup, 1 = running, 2 = pre-startup + // Now it makes more sense to list them in this logical order as opposed to + // numerical order: + // 2 = pre-startup, 1 = startup, 0 = startup + // This only matters for "register info" but let's trust what the server + // chose regardless. + std::map<uint64_t, FieldEnum::Enumerator> enumerators; + + enum_node.ForEachChildElementWithName( + "evalue", [&enumerators, &log](const XMLNode &enumerator_node) { + std::optional<llvm::StringRef> name; + std::optional<uint64_t> value; + + enumerator_node.ForEachAttribute( + [&name, &value, &log](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "name") { + if (attr_value.size()) + name = attr_value; + else + LLDB_LOG(log, "ProcessGDBRemote::ParseEnumEvalues " + "Ignoring empty name in evalue"); + } else if (attr_name == "value") { + uint64_t parsed_value = 0; + if (llvm::to_integer(attr_value, parsed_value)) + value = parsed_value; + else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues " + "Invalid value \"{0}\" in " + "evalue", + attr_value.data()); + } else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues Ignoring " + "unknown attribute " + "\"{0}\" in evalue", + attr_name.data()); + + // Keep walking attributes. + return true; + }); + + if (value && name) + enumerators.insert_or_assign( + *value, FieldEnum::Enumerator(*value, name->str())); + + // Find all evalue elements. + return true; + }); + + FieldEnum::Enumerators final_enumerators; + for (auto [_, enumerator] : enumerators) + final_enumerators.push_back(enumerator); + + return final_enumerators; +} + +static void +ParseEnums(XMLNode feature_node, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + Log *log(GetLog(GDBRLog::Process)); + + // The top level element is "<enum...". + feature_node.ForEachChildElementWithName( + "enum", [log, ®isters_enum_types](const XMLNode &enum_node) { + std::string id; + + enum_node.ForEachAttribute([&id](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "id") + id = attr_value; + + // There is also a "size" attribute that is supposed to be the size in + // bytes of the register this applies to. However: + // * LLDB doesn't need this information. + // * It is difficult to verify because you have to wait until the + // enum is applied to a field. + // + // So we will emit this attribute in XML for GDB's sake, but will not + // bother ingesting it. + + // Walk all attributes. + return true; + }); + + if (!id.empty()) { + FieldEnum::Enumerators enumerators = ParseEnumEvalues(enum_node); + if (!enumerators.empty()) { + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnums Found enum type \"{0}\"", + id); + registers_enum_types.insert_or_assign( + id, std::make_unique<FieldEnum>(id, enumerators)); + } + } + + // Find all <enum> elements. + return true; + }); +} + +static std::vector<RegisterFlags::Field> ParseFlagsFields( + XMLNode flags_node, unsigned size, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { Log *log(GetLog(GDBRLog::Process)); const unsigned max_start_bit = size * 8 - 1; // Process the fields of this set of flags. std::vector<RegisterFlags::Field> fields; - flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, - &log](const XMLNode - &field_node) { + flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, &log, + ®isters_enum_types]( + const XMLNode + &field_node) { std::optional<llvm::StringRef> name; std::optional<unsigned> start; std::optional<unsigned> end; + std::optional<llvm::StringRef> type; - field_node.ForEachAttribute([&name, &start, &end, max_start_bit, + field_node.ForEachAttribute([&name, &start, &end, &type, max_start_bit, &log](const llvm::StringRef &attr_name, const llvm::StringRef &attr_value) { // Note that XML in general requires that each of these attributes only @@ -4229,8 +4353,7 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, attr_value.data()); } } else if (attr_name == "type") { - // Type is a known attribute but we do not currently use it and it is - // not required. + type = attr_value; } else { LLDB_LOG( log, @@ -4243,14 +4366,55 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, }); if (name && start && end) { - if (*start > *end) { + if (*start > *end) LLDB_LOG( log, "ProcessGDBRemote::ParseFlagsFields Start {0} > end {1} in field " "\"{2}\", ignoring", *start, *end, name->data()); - } else { - fields.push_back(RegisterFlags::Field(name->str(), *start, *end)); + else { + if (RegisterFlags::Field::GetSizeInBits(*start, *end) > 64) + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Ignoring field \"{2}\" " + "that has " + "size > 64 bits, this is not supported", + name->data()); + else { + // A field's type may be set to the name of an enum type. + const FieldEnum *enum_type = nullptr; + if (type && !type->empty()) { + auto found = registers_enum_types.find(*type); + if (found != registers_enum_types.end()) { + enum_type = found->second.get(); + + // No enumerator can exceed the range of the field itself. + uint64_t max_value = + RegisterFlags::Field::GetMaxValue(*start, *end); + for (const auto &enumerator : enum_type->GetEnumerators()) { + if (enumerator.m_value > max_value) { + enum_type = nullptr; + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields In enum \"{0}\" " + "evalue \"{1}\" with value {2} exceeds the maximum value " + "of field \"{3}\" ({4}), ignoring enum", + type->data(), enumerator.m_name, enumerator.m_value, + name->data(), max_value); + break; + } + } + } else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Could not find type " + "\"{0}\" " + "for field \"{1}\", ignoring", + type->data(), name->data()); + } + } + + fields.push_back( + RegisterFlags::Field(name->str(), *start, *end, enum_type)); + } } } @@ -4261,12 +4425,14 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, void ParseFlags( XMLNode feature_node, - llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types) { + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { Log *log(GetLog(GDBRLog::Process)); feature_node.ForEachChildElementWithName( "flags", - [&log, ®isters_flags_types](const XMLNode &flags_node) -> bool { + [&log, ®isters_flags_types, + ®isters_enum_types](const XMLNode &flags_node) -> bool { LLDB_LOG(log, "ProcessGDBRemote::ParseFlags Found flags node \"{0}\"", flags_node.GetAttributeValue("id").c_str()); @@ -4299,7 +4465,7 @@ void ParseFlags( if (id && size) { // Process the fields of this set of flags. std::vector<RegisterFlags::Field> fields = - ParseFlagsFields(flags_node, *size); + ParseFlagsFields(flags_node, *size, registers_enum_types); if (fields.size()) { // Sort so that the fields with the MSBs are first. std::sort(fields.rbegin(), fields.rend()); @@ -4364,15 +4530,21 @@ void ParseFlags( bool ParseRegisters( XMLNode feature_node, GdbServerTargetInfo &target_info, std::vector<DynamicRegisterInfo::Register> ®isters, - llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types) { + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { if (!feature_node) return false; Log *log(GetLog(GDBRLog::Process)); - ParseFlags(feature_node, registers_flags_types); + // Enums first because they are referenced by fields in the flags. + ParseEnums(feature_node, registers_enum_types); + for (const auto &enum_type : registers_enum_types) + enum_type.second->DumpToLog(log); + + ParseFlags(feature_node, registers_flags_types, registers_enum_types); for (const auto &flags : registers_flags_types) - flags.second->log(log); + flags.second->DumpToLog(log); feature_node.ForEachChildElementWithName( "reg", @@ -4620,6 +4792,8 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( // 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") + .Case("riscv:rv64", "riscv64") + .Case("riscv:rv32", "riscv32") .Default(target_info.arch) + "--"); @@ -4630,7 +4804,7 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( if (arch_to_use.IsValid()) { for (auto &feature_node : feature_nodes) { ParseRegisters(feature_node, target_info, registers, - m_registers_flags_types); + m_registers_flags_types, m_registers_enum_types); } for (const auto &include : target_info.includes) { @@ -4695,16 +4869,19 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { if (!m_gdb_comm.GetQXferFeaturesReadSupported()) return false; - // This holds register flags information for the whole of target.xml. + // These hold register type information for the whole of target.xml. // target.xml may include further documents that // GetGDBServerRegisterInfoXMLAndProcess will recurse to fetch and process. // That's why we clear the cache here, and not in // GetGDBServerRegisterInfoXMLAndProcess. To prevent it being cleared on every // include read. m_registers_flags_types.clear(); + m_registers_enum_types.clear(); std::vector<DynamicRegisterInfo::Register> registers; if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml", - registers)) + registers) && + // Target XML is not required to include register information. + !registers.empty()) AddRemoteRegisters(registers, arch_to_use); return m_register_info_sp->GetNumRegisters() > 0; @@ -5069,7 +5246,7 @@ std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( llvm::StringRef usec_name, usec_value; uint32_t input_file_pos = profileDataExtractor.GetFilePos(); if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { - if (usec_name.equals("thread_used_usec")) { + if (usec_name == "thread_used_usec") { has_used_usec = true; usec_value.getAsInteger(0, curr_used_usec); } else { @@ -5279,8 +5456,10 @@ public: (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { - StreamSP output_stream_sp( - m_interpreter.GetDebugger().GetAsyncOutputStream()); + StreamSP output_stream_sp = result.GetImmediateOutputStream(); + if (!output_stream_sp) + output_stream_sp = + StreamSP(m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream(output_stream_sp); const uint32_t num_packets = @@ -5340,8 +5519,7 @@ public: interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", nullptr) { - CommandArgumentData max_arg{eArgTypeUnsignedInteger, eArgRepeatPlain}; - m_arguments.push_back({max_arg}); + AddSimpleArgumentList(eArgTypeUnsignedInteger); } ~CommandObjectProcessGDBRemotePacketXferSize() override = default; @@ -5383,8 +5561,7 @@ public: "be added to the packet prior to sending and " "stripped from the result.", nullptr) { - CommandArgumentData packet_arg{eArgTypeNone, eArgRepeatStar}; - m_arguments.push_back({packet_arg}); + AddSimpleArgumentList(eArgTypeNone, eArgRepeatStar); } ~CommandObjectProcessGDBRemotePacketSend() override = default; @@ -5622,8 +5799,11 @@ void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { Log *log = GetLog(GDBRLog::Process); - assert(!m_vfork_in_progress); - m_vfork_in_progress = true; + LLDB_LOG( + log, + "ProcessGDBRemote::DidFork() called for child_pid: {0}, child_tid {1}", + child_pid, child_tid); + ++m_vfork_in_progress_count; // Disable all software breakpoints for the duration of vfork. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) @@ -5677,8 +5857,8 @@ void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { } void ProcessGDBRemote::DidVForkDone() { - assert(m_vfork_in_progress); - m_vfork_in_progress = false; + assert(m_vfork_in_progress_count > 0); + --m_vfork_in_progress_count; // Reenable all software breakpoints that were enabled before vfork. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) @@ -5688,7 +5868,9 @@ void ProcessGDBRemote::DidVForkDone() { 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; + if (GetFollowForkMode() == eFollowChild) { + if (m_vfork_in_progress_count > 0) + --m_vfork_in_progress_count; + } Process::DidExec(); } |