Skip to content

Commit dd5b17f

Browse files
[LLDB] Add a way to avoid manual stepping over breakpoints when resuming
Currently, the LLDB client will try to step over breakpoints as part of the resume operation. However, some servers don't need that functionality because of performance reasons (e.g. there are too many threads to step over). This has been mentioned in https://discourse.llvm.org/t/lldb-resume-without-single-stepping-over-breakpoints/88538 This patch adds an lldb-server extension that disables the default behavior of LLDB. I ended up using the extension list instead of qHostInfo because qHostInfo is currently building a static list, and this feature is more of a feature of the server than of the platform or host.
1 parent 9b02901 commit dd5b17f

File tree

9 files changed

+82
-41
lines changed

9 files changed

+82
-41
lines changed

lldb/include/lldb/Host/common/NativeProcessProtocol.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,9 @@ class NativeProcessProtocol {
264264
memory_tagging = (1u << 6),
265265
savecore = (1u << 7),
266266
siginfo_read = (1u << 8),
267+
resume_without_disabling_breakpoints = (1u << 9),
267268

268-
LLVM_MARK_AS_BITMASK_ENUM(siginfo_read)
269+
LLVM_MARK_AS_BITMASK_ENUM(resume_without_disabling_breakpoints)
269270
};
270271

271272
class Manager {

lldb/include/lldb/Target/Process.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,6 +3076,8 @@ void PruneThreadPlans();
30763076
/// false otherwise.
30773077
virtual bool SupportsMemoryTagging() { return false; }
30783078

3079+
virtual bool SupportsResumeWithoutDisablingBreakpoints() { return false; }
3080+
30793081
/// Does the final operation to read memory tags. E.g. sending a GDB packet.
30803082
/// It assumes that ReadMemoryTags has checked that memory tagging is enabled
30813083
/// and has expanded the memory range as needed.

lldb/include/lldb/Target/ThreadList.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ class ThreadList : public ThreadCollection {
126126
///
127127
bool WillResume(lldb::RunDirection &direction);
128128

129+
/// Set up a step-over breakpoint plan before resuming if needed..
130+
///
131+
/// \param[in] thread_to_run
132+
/// An optional thread that will have the run priority in the resume.
133+
///
134+
/// \param[in] direction
135+
/// The direction to run in.
136+
void
137+
SetUpStepOverBreakpointBeforeResumeIfNeeded(lldb::ThreadSP thread_to_run,
138+
lldb::RunDirection &direction);
139+
129140
void DidResume();
130141

131142
void DidStop();

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
373373
m_supports_reverse_continue = eLazyBoolNo;
374374
m_supports_reverse_step = eLazyBoolNo;
375375
m_supports_multi_mem_read = eLazyBoolNo;
376+
m_supports_resume_without_disabling_breakpoints = eLazyBoolNo;
376377

377378
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
378379
// not, we assume no limit
@@ -434,6 +435,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
434435
m_supports_reverse_step = eLazyBoolYes;
435436
else if (x == "MultiMemRead+")
436437
m_supports_multi_mem_read = eLazyBoolYes;
438+
else if (x == "resume-without-disabling-breakpoints+")
439+
m_supports_resume_without_disabling_breakpoints = eLazyBoolYes;
437440
// Look for a list of compressions in the features list e.g.
438441
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
439442
// deflate,lzma
@@ -686,6 +689,13 @@ bool GDBRemoteCommunicationClient::GetMemoryTaggingSupported() {
686689
return m_supports_memory_tagging == eLazyBoolYes;
687690
}
688691

692+
bool GDBRemoteCommunicationClient::
693+
GetResumeWithoutDisablingBreakpointsSupported() {
694+
if (m_supports_resume_without_disabling_breakpoints == eLazyBoolCalculate)
695+
GetRemoteQSupported();
696+
return m_supports_resume_without_disabling_breakpoints == eLazyBoolYes;
697+
}
698+
689699
DataBufferSP GDBRemoteCommunicationClient::ReadMemoryTags(lldb::addr_t addr,
690700
size_t len,
691701
int32_t type) {

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,8 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
445445

446446
bool GetMemoryTaggingSupported();
447447

448+
bool GetResumeWithoutDisablingBreakpointsSupported();
449+
448450
bool UsesNativeSignals();
449451

450452
lldb::DataBufferSP ReadMemoryTags(lldb::addr_t addr, size_t len,
@@ -577,6 +579,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
577579
LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
578580
LazyBool m_supports_reverse_step = eLazyBoolCalculate;
579581
LazyBool m_supports_multi_mem_read = eLazyBoolCalculate;
582+
LazyBool m_supports_resume_without_disabling_breakpoints = eLazyBoolCalculate;
580583

581584
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
582585
m_supports_qUserName : 1, m_supports_qGroupName : 1,

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4326,6 +4326,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
43264326
ret.push_back("memory-tagging+");
43274327
if (bool(plugin_features & Extension::savecore))
43284328
ret.push_back("qSaveCore+");
4329+
if (bool(plugin_features & Extension::resume_without_disabling_breakpoints))
4330+
ret.push_back("resume-without-disabling-breakpoints+");
43294331

43304332
// check for client features
43314333
m_extensions_supported = {};

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2869,6 +2869,10 @@ bool ProcessGDBRemote::SupportsMemoryTagging() {
28692869
return m_gdb_comm.GetMemoryTaggingSupported();
28702870
}
28712871

2872+
bool ProcessGDBRemote::SupportsResumeWithoutDisablingBreakpoints() {
2873+
return m_gdb_comm.GetResumeWithoutDisablingBreakpointsSupported();
2874+
}
2875+
28722876
llvm::Expected<std::vector<uint8_t>>
28732877
ProcessGDBRemote::DoReadMemoryTags(lldb::addr_t addr, size_t len,
28742878
int32_t type) {

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ class ProcessGDBRemote : public Process,
266266

267267
bool SupportsMemoryTagging() override;
268268

269+
/// \returns true if the process doesn't need the client to disable
270+
/// breakpoints before issuing a resume operation.
271+
bool SupportsResumeWithoutDisablingBreakpoints() override;
272+
269273
/// Broadcaster event bits definitions.
270274
enum {
271275
eBroadcastBitAsyncContinue = (1 << 0),

lldb/source/Target/ThreadList.cpp

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,49 @@ void ThreadList::DiscardThreadPlans() {
494494
(*pos)->DiscardThreadPlans(true);
495495
}
496496

497+
void ThreadList::SetUpStepOverBreakpointBeforeResumeIfNeeded(
498+
ThreadSP thread_to_run, RunDirection &direction) {
499+
// If the process doesn't need the client to disable breakpoints before
500+
// issuing a resume operation, we can skip the step-over breakpoint plan
501+
// setup.
502+
if (!m_process.SupportsResumeWithoutDisablingBreakpoints())
503+
return;
504+
505+
// Give all the threads that are likely to run a chance to set up their
506+
// step over breakpoint behavior.
507+
if (thread_to_run != nullptr) {
508+
if (thread_to_run->SetupToStepOverBreakpointIfNeeded(direction)) {
509+
// We only need to step over breakpoints when running forward, and the
510+
// step-over-breakpoint plan itself wants to run forward, so this
511+
// keeps our desired direction.
512+
assert(thread_to_run->GetCurrentPlan()->GetDirection() == direction);
513+
}
514+
} else {
515+
collection::iterator end = m_threads.end();
516+
for (auto pos = m_threads.begin(); pos != end; ++pos) {
517+
ThreadSP thread_sp(*pos);
518+
if (thread_sp->GetResumeState() != eStateSuspended) {
519+
if (thread_sp->IsOperatingSystemPluginThread() &&
520+
!thread_sp->GetBackingThread())
521+
continue;
522+
if (thread_sp->SetupToStepOverBreakpointIfNeeded(direction)) {
523+
// We only need to step over breakpoints when running forward, and the
524+
// step-over-breakpoint plan itself wants to run forward, so this
525+
// keeps our desired direction.
526+
assert(thread_sp->GetCurrentPlan()->GetDirection() == direction);
527+
// You can't say "stop others" and also want yourself to be suspended.
528+
assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
529+
thread_to_run = thread_sp;
530+
if (thread_sp->ShouldRunBeforePublicStop()) {
531+
// This takes precedence, so if we find one of these, service it:
532+
break;
533+
}
534+
}
535+
}
536+
}
537+
}
538+
}
539+
497540
bool ThreadList::WillResume(RunDirection &direction) {
498541
// Run through the threads and perform their momentary actions. But we only
499542
// do this for threads that are running, user suspended threads stay where
@@ -558,46 +601,7 @@ bool ThreadList::WillResume(RunDirection &direction) {
558601
direction = m_process.GetBaseDirection();
559602
}
560603

561-
// Give all the threads that are likely to run a last chance to set up their
562-
// state before we negotiate who is actually going to get a chance to run...
563-
// Don't set to resume suspended threads, and if any thread wanted to stop
564-
// others, only call setup on the threads that request StopOthers...
565-
if (thread_to_run != nullptr) {
566-
// See if any thread wants to run stopping others. If it does, then we
567-
// won't setup the other threads for resume, since they aren't going to get
568-
// a chance to run. This is necessary because the SetupForResume might add
569-
// "StopOthers" plans which would then get to be part of the who-gets-to-run
570-
// negotiation, but they're coming in after the fact, and the threads that
571-
// are already set up should take priority.
572-
if (thread_to_run->SetupToStepOverBreakpointIfNeeded(direction)) {
573-
// We only need to step over breakpoints when running forward, and the
574-
// step-over-breakpoint plan itself wants to run forward, so this
575-
// keeps our desired direction.
576-
assert(thread_to_run->GetCurrentPlan()->GetDirection() == direction);
577-
}
578-
} else {
579-
for (pos = m_threads.begin(); pos != end; ++pos) {
580-
ThreadSP thread_sp(*pos);
581-
if (thread_sp->GetResumeState() != eStateSuspended) {
582-
if (thread_sp->IsOperatingSystemPluginThread() &&
583-
!thread_sp->GetBackingThread())
584-
continue;
585-
if (thread_sp->SetupToStepOverBreakpointIfNeeded(direction)) {
586-
// We only need to step over breakpoints when running forward, and the
587-
// step-over-breakpoint plan itself wants to run forward, so this
588-
// keeps our desired direction.
589-
assert(thread_sp->GetCurrentPlan()->GetDirection() == direction);
590-
// You can't say "stop others" and also want yourself to be suspended.
591-
assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
592-
thread_to_run = thread_sp;
593-
if (thread_sp->ShouldRunBeforePublicStop()) {
594-
// This takes precedence, so if we find one of these, service it:
595-
break;
596-
}
597-
}
598-
}
599-
}
600-
}
604+
SetUpStepOverBreakpointBeforeResumeIfNeeded(thread_to_run, direction);
601605

602606
if (thread_to_run != nullptr) {
603607
Log *log = GetLog(LLDBLog::Step);

0 commit comments

Comments
 (0)