Disable ThreadPlanSingleThreadTimeout during step over breakpoint (#104532)

This PR fixes another race condition in
https://github.com/llvm/llvm-project/pull/90930. The failure was found
by @labath with this log: https://paste.debian.net/hidden/30235a5c/:
```
dotest_wrapper.  <  15> send packet: $z0,224505,1#65
...
b-remote.async>  <  22> send packet: $vCont;s:p1dcf.1dcf#4c
intern-state     GDBRemoteClientBase::Lock::Lock sent packet: \x03
b-remote.async>  < 818> read packet: $T13thread:p1dcf.1dcf;name:a.out;threads:1dcf,1dd2;jstopinfo:5b7b226e616d65223a22612e6f7574222c22726561736f6e223a227369676e616c222c227369676e616c223a31392c22746964223a373633317d2c7b226e616d65223a22612e6f7574222c22746964223a373633347d5d;thread-pcs:0000000000224505,00007f4e4302119a;00:0000000000000000;01:0000000000000000;02:0100000000000000;03:0000000000000000;04:9084997dfc7f0000;05:a8742a0000000000;06:b084997dfc7f0000;07:6084997dfc7f0000;08:0000000000000000;09:00d7e5424e7f0000;0a:d0d9e5424e7f0000;0b:0202000000000000;0c:80cc290000000000;0d:d8cc1c434e7f0000;0e:2886997dfc7f0000;0f:0100000000000000;10:0545220000000000;11:0602000000000000;12:3300000000000000;13:0000000000000000;14:0000000000000000;15:2b00000000000000;16:80fbe5424e7f0000;17:0000000000000000;18:0000000000000000;19:0000000000000000;reason:signal;#b9
```
It shows an async interrupt "\x03" was sent immediately after `vCont;s`
single step over breakpoint at address `0x224505` (which was disabled
before vCont). And the later stop was still at the original PC
(0x224505) not moving forward.

The investigation shows the failure happens when timeout is short and
async interrupt is sent to lldb-server immediately after vCont so
ptrace() resumes and then async interrupts debuggee immediately so
debuggee does not get a chance to execute and move PC. So it enters stop
mode immediately at original PC. `ThreadPlanStepOverBreakpoint` does not
expect PC not moving and reports stop at the original place.

To fix this, the PR prevents `ThreadPlanSingleThreadTimeout` from being
created during `ThreadPlanStepOverBreakpoint` by introduces a new
`SupportsResumeOthers()` method and `ThreadPlanStepOverBreakpoint`
returns false for it. This makes sense because we should never resume
threads during step over breakpoint anyway otherwise it might cause
other threads to miss breakpoint.

---------

Co-authored-by: jeffreytan81 <jeffreytan@fb.com>
This commit is contained in:
jeffreytan81
2024-08-28 13:34:35 -07:00
committed by GitHub
parent 17b7a9da46
commit 38b252aa45
4 changed files with 21 additions and 1 deletions

View File

@@ -385,7 +385,13 @@ public:
virtual void SetStopOthers(bool new_value);
virtual bool StopOthers();
// Returns true if the thread plan supports ThreadPlanSingleThreadTimeout to
// resume other threads after timeout. If the thread plan returns false it
// will prevent ThreadPlanSingleThreadTimeout from being created when this
// thread plan is alive.
virtual bool SupportsResumeOthers() { return true; }
virtual bool ShouldRunBeforePublicStop() { return false; }
// This is the wrapper for DoWillResume that does generic ThreadPlan logic,

View File

@@ -23,6 +23,7 @@ public:
void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
bool ValidatePlan(Stream *error) override;
bool ShouldStop(Event *event_ptr) override;
bool SupportsResumeOthers() override;
bool StopOthers() override;
lldb::StateType GetPlanRunState() override;
bool WillStop() override;

View File

@@ -76,6 +76,9 @@ void ThreadPlanSingleThreadTimeout::PushNewWithTimeout(Thread &thread,
if (!thread.GetCurrentPlan()->StopOthers())
return;
if (!thread.GetCurrentPlan()->SupportsResumeOthers())
return;
auto timeout_plan = new ThreadPlanSingleThreadTimeout(thread, info);
ThreadPlanSP thread_plan_sp(timeout_plan);
auto status = thread.QueueThreadPlan(thread_plan_sp,
@@ -102,6 +105,9 @@ void ThreadPlanSingleThreadTimeout::ResumeFromPrevState(Thread &thread,
if (!thread.GetCurrentPlan()->StopOthers())
return;
if (!thread.GetCurrentPlan()->SupportsResumeOthers())
return;
auto timeout_plan = new ThreadPlanSingleThreadTimeout(thread, info);
ThreadPlanSP thread_plan_sp(timeout_plan);
auto status = thread.QueueThreadPlan(thread_plan_sp,

View File

@@ -103,6 +103,13 @@ bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) {
bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; }
// This thread plan does a single instruction step over a breakpoint instruction
// and needs to not resume other threads, so return false to stop the
// ThreadPlanSingleThreadTimeout from timing out and trying to resume all
// threads. If all threads gets resumed before we disable, single step and
// re-enable the breakpoint, we can miss breakpoints on other threads.
bool ThreadPlanStepOverBreakpoint::SupportsResumeOthers() { return false; }
StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() {
return eStateStepping;
}