aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
diff options
context:
space:
mode:
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.cpp338
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>> &registers_enum_types) {
+ Log *log(GetLog(GDBRLog::Process));
+
+ // The top level element is "<enum...".
+ feature_node.ForEachChildElementWithName(
+ "enum", [log, &registers_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>> &registers_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,
+ &registers_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>> &registers_flags_types) {
+ llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types,
+ const llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_enum_types) {
Log *log(GetLog(GDBRLog::Process));
feature_node.ForEachChildElementWithName(
"flags",
- [&log, &registers_flags_types](const XMLNode &flags_node) -> bool {
+ [&log, &registers_flags_types,
+ &registers_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> &registers,
- llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types) {
+ llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types,
+ llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_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();
}