[lldb-server] Reset stop reason of all threads when resuming

Summary:
This patch makes the stop reason reset logic similar to MacOS' debugserver, where exceptions are reset for all threads when resuming process for stepping or continuing (see [[ 96f3ea0d21/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp (L433) | MachThreadList::ProcessWillResume ]] and [[ 96f3ea0d21/lldb/tools/debugserver/source/MacOSX/MachThread.cpp (L363) | MachThread::ThreadWillResume ]]).

Resetting stop reasons on resume fixes problems where LLDB spuriously reports SIGTRAP signal stop reason for deleted breakpoints (both internal and public) and where  LLDB stops on an internal breakpoint while stepping over while a breakpoint is hit in another thread. See [[ https://bugs.llvm.org/show_bug.cgi?id=45642 | PR45642 ]] for details.

Reviewed By: jingham, labath

Differential Revision: https://reviews.llvm.org/D79308
This commit is contained in:
Jaroslav Sevcik 2020-05-20 10:59:57 +02:00 committed by Pavel Labath
parent 51446c13f6
commit 56de738d18
6 changed files with 101 additions and 0 deletions

View File

@ -1062,6 +1062,8 @@ Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) {
if (action == nullptr) {
LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
thread->GetID());
// Make sure we reset the stop reason for all the threads.
static_cast<NativeThreadLinux &>(*thread).ResetStopReason();
continue;
}

View File

@ -396,7 +396,10 @@ void NativeThreadLinux::SetStoppedByTrace() {
void NativeThreadLinux::SetStoppedWithNoReason() {
SetStopped();
ResetStopReason();
}
void NativeThreadLinux::ResetStopReason() {
m_stop_info.reason = StopReason::eStopReasonNone;
m_stop_info.details.signal.signo = 0;
}

View File

@ -94,6 +94,8 @@ private:
void SetStopped();
void ResetStopReason();
// Member Variables
lldb::StateType m_state;
ThreadStopInfo m_stop_info;

View File

@ -0,0 +1,4 @@
CXX_SOURCES := main.cpp
ENABLE_THREADS := YES
include Makefile.rules

View File

@ -0,0 +1,63 @@
"""
Test stop reasons after hitting and deleting a breakpoint and
stepping another thread. Scenario:
- run a thread
- stop the thread at a breakpoint
- delete the breakpoint
- single step on the main thread
The thread stopped at the deleted breakpoint should have stop reason
'none'.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class ThreadBreakStepOtherTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
def test_hit_breakpoint_delete_step_other_thread(self):
main_source_file = lldb.SBFileSpec("main.cpp")
self.build()
(target, process, main_thread, _) = lldbutil.run_to_source_breakpoint(
self, "// main break here", main_source_file, only_one_thread = False)
# Run until the breakpoint in the thread.
thread_breakpoint = target.BreakpointCreateBySourceRegex(
"// thread break here", main_source_file)
self.assertGreater(
thread_breakpoint.GetNumLocations(),
0,
"thread breakpoint has no locations associated with it.")
process.Continue()
stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(
process, thread_breakpoint)
self.assertEquals(
1,
len(stopped_threads),
"only one thread expected stopped at the thread breakpoint")
breakpoint_thread = stopped_threads[0]
# Delete the breakpint in the thread and do a step in the main thread.
target.BreakpointDelete(thread_breakpoint.GetID())
main_thread.StepInstruction(False)
# Check the stop reasons.
reason = main_thread.GetStopReason()
self.assertEqual(
lldb.eStopReasonPlanComplete,
reason,
"Expected thread stop reason 'plancomplete', but got '%s'" %
lldbutil.stop_reason_to_str(reason))
reason = breakpoint_thread.GetStopReason()
self.assertEqual(
lldb.eStopReasonNone,
reason,
"Expected thread stop reason 'none', but got '%s'" %
lldbutil.stop_reason_to_str(reason))

View File

@ -0,0 +1,27 @@
#include <thread>
#include "pseudo_barrier.h"
// Barrier for starting the thread and reaching the loop in main.
pseudo_barrier_t g_barrier;
volatile int g_foo = 0;
void thread_func() {
// Wait until all the threads are running
pseudo_barrier_wait(g_barrier);
g_foo = 1; // thread break here
}
int main() {
g_foo = 0; // main break here
pseudo_barrier_init(g_barrier, 2);
std::thread t(thread_func);
pseudo_barrier_wait(g_barrier);
// A dummy loop to have something to step through.
volatile int i = 0;
while (g_foo == 0)
++i;
t.join();
return 0;
}