[Lldb-commits] [lldb] Refactor `ThreadList::WillResume()` to prepare to support reverse execution (PR #120817)

Robert O'Callahan via lldb-commits lldb-commits at lists.llvm.org
Fri Dec 20 18:59:08 PST 2024


https://github.com/rocallahan updated https://github.com/llvm/llvm-project/pull/120817

>From b021a56268c74010eae017d2d1169ab8b79978b3 Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Tue, 17 Dec 2024 23:40:34 +0000
Subject: [PATCH 1/7] [lldb] Move thread-notification setup to happen later

This lets us check `run_me_only_list.GetSize()` instead of `wants_solo_run`.
---
 lldb/source/Target/ThreadList.cpp | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 1a2d7dd61c778f..b44e53e0673b47 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -540,20 +540,6 @@ bool ThreadList::WillResume() {
     }
   }
 
-  if (wants_solo_run) {
-    Log *log = GetLog(LLDBLog::Step);
-    if (log && log->GetVerbose())
-      LLDB_LOGF(log, "Turning on notification of new threads while single "
-                     "stepping a thread.");
-    m_process.StartNoticingNewThreads();
-  } else {
-    Log *log = GetLog(LLDBLog::Step);
-    if (log && log->GetVerbose())
-      LLDB_LOGF(log, "Turning off notification of new threads while single "
-                     "stepping a thread.");
-    m_process.StopNoticingNewThreads();
-  }
-
   // Give all the threads that are likely to run a last chance to set up their
   // state before we negotiate who is actually going to get a chance to run...
   // Don't set to resume suspended threads, and if any thread wanted to stop
@@ -607,6 +593,20 @@ bool ThreadList::WillResume() {
     }
   }
 
+  if (run_me_only_list.GetSize(false) == 0) {
+    Log *log = GetLog(LLDBLog::Step);
+    if (log && log->GetVerbose())
+      LLDB_LOGF(log, "Turning on notification of new threads while single "
+                     "stepping a thread.");
+    m_process.StartNoticingNewThreads();
+  } else {
+    Log *log = GetLog(LLDBLog::Step);
+    if (log && log->GetVerbose())
+      LLDB_LOGF(log, "Turning off notification of new threads while single "
+                     "stepping a thread.");
+    m_process.StopNoticingNewThreads();
+  }
+
   bool need_to_resume = true;
 
   if (run_me_only_list.GetSize(false) == 0) {

>From 5164b418ea8e606729b24d0705f70e040aed8757 Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Tue, 17 Dec 2024 23:46:01 +0000
Subject: [PATCH 2/7] [lldb] Make `Thread::WillResume()` report whether it
 pushed a `ThreadPlanStepOverBreakpoint`

We'll need this later to determine whether we need to account for
the newly-pushed `ThreadPlanStepOverBreakpoint`.
---
 lldb/include/lldb/Target/Thread.h | 13 ++++++++-----
 lldb/source/Target/Thread.cpp     |  6 ++++--
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 38b65b2bc58490..ef66fa11574db9 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -200,11 +200,14 @@ class Thread : public std::enable_shared_from_this<Thread>,
   ///    The User resume state for this thread.
   lldb::StateType GetResumeState() const { return m_resume_state; }
 
-  // This function is called on all the threads before "ShouldResume" and
-  // "WillResume" in case a thread needs to change its state before the
-  // ThreadList polls all the threads to figure out which ones actually will
-  // get to run and how.
-  void SetupForResume();
+  /// This function is called on all the threads before "ShouldResume" and
+  /// "WillResume" in case a thread needs to change its state before the
+  /// ThreadList polls all the threads to figure out which ones actually will
+  /// get to run and how.
+  ///
+  /// \return
+  ///    True if we pushed a ThreadPlanStepOverBreakpoint
+  bool SetupForResume();
 
   // Do not override this function, it is for thread plan logic only
   bool ShouldResume(lldb::StateType resume_state);
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index a6130f6b925bbf..b5261310970611 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -617,7 +617,7 @@ void Thread::WillStop() {
   current_plan->WillStop();
 }
 
-void Thread::SetupForResume() {
+bool Thread::SetupForResume() {
   if (GetResumeState() != eStateSuspended) {
     // First check whether this thread is going to "actually" resume at all.
     // For instance, if we're stepping from one level to the next of an
@@ -625,7 +625,7 @@ void Thread::SetupForResume() {
     // without actually running this thread.  In that case, for this thread we
     // shouldn't push a step over breakpoint plan or do that work.
     if (GetCurrentPlan()->IsVirtualStep())
-      return;
+      return false;
 
     // If we're at a breakpoint push the step-over breakpoint plan.  Do this
     // before telling the current plan it will resume, since we might change
@@ -663,11 +663,13 @@ void Thread::SetupForResume() {
               step_bp_plan->SetAutoContinue(true);
             }
             QueueThreadPlan(step_bp_plan_sp, false);
+            return true;
           }
         }
       }
     }
   }
+  return false;
 }
 
 bool Thread::ShouldResume(StateType resume_state) {

>From 0fe7e983f0c194dddb22059c4dd054dcb55d98a6 Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Tue, 17 Dec 2024 23:47:54 +0000
Subject: [PATCH 3/7] [lldb] Use `thread_sp` in more places in
 `ThreadList::WillResume()`

This makes the code prettier and more consistent.
---
 lldb/source/Target/ThreadList.cpp | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index b44e53e0673b47..676596978cf8cf 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -528,12 +528,13 @@ bool ThreadList::WillResume() {
   bool wants_solo_run = false;
 
   for (pos = m_threads.begin(); pos != end; ++pos) {
+    ThreadSP thread_sp(*pos);
     lldbassert((*pos)->GetCurrentPlan() &&
                "thread should not have null thread plan");
-    if ((*pos)->GetResumeState() != eStateSuspended &&
-        (*pos)->GetCurrentPlan()->StopOthers()) {
-      if ((*pos)->IsOperatingSystemPluginThread() &&
-          !(*pos)->GetBackingThread())
+    if (thread_sp->GetResumeState() != eStateSuspended &&
+        thread_sp->GetCurrentPlan()->StopOthers()) {
+      if (thread_sp->IsOperatingSystemPluginThread() &&
+          !thread_sp->GetBackingThread())
         continue;
       wants_solo_run = true;
       break;
@@ -546,12 +547,13 @@ bool ThreadList::WillResume() {
   // others, only call setup on the threads that request StopOthers...
 
   for (pos = m_threads.begin(); pos != end; ++pos) {
-    if ((*pos)->GetResumeState() != eStateSuspended &&
+    ThreadSP thread_sp(*pos);
+    if (thread_sp->GetResumeState() != eStateSuspended &&
         (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) {
-      if ((*pos)->IsOperatingSystemPluginThread() &&
-          !(*pos)->GetBackingThread())
+      if (thread_sp->IsOperatingSystemPluginThread() &&
+          !thread_sp->GetBackingThread())
         continue;
-      (*pos)->SetupForResume();
+      thread_sp->SetupForResume();
     }
   }
 
@@ -574,8 +576,8 @@ bool ThreadList::WillResume() {
     ThreadSP thread_sp(*pos);
     if (thread_sp->GetResumeState() != eStateSuspended &&
         thread_sp->GetCurrentPlan()->StopOthers()) {
-      if ((*pos)->IsOperatingSystemPluginThread() &&
-          !(*pos)->GetBackingThread())
+      if (thread_sp->IsOperatingSystemPluginThread() &&
+          !thread_sp->GetBackingThread())
         continue;
 
       // You can't say "stop others" and also want yourself to be suspended.

>From 070a2561e5ee3e7b083926ac1b1ff7fb3c2483ca Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Tue, 17 Dec 2024 23:55:19 +0000
Subject: [PATCH 4/7] [lldb] Move `SetupForResume()` calls to happen after the
 first pass populating `run_me_only_list`

Eventually we will need to determine the execution direction after populating
`run_me_only_list` and before calling `SetupForResume()`.
---
 lldb/source/Target/ThreadList.cpp | 46 ++++++++++++++++++++-----------
 1 file changed, 30 insertions(+), 16 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 676596978cf8cf..8edbd13c64a8b8 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -541,22 +541,6 @@ bool ThreadList::WillResume() {
     }
   }
 
-  // Give all the threads that are likely to run a last chance to set up their
-  // state before we negotiate who is actually going to get a chance to run...
-  // Don't set to resume suspended threads, and if any thread wanted to stop
-  // others, only call setup on the threads that request StopOthers...
-
-  for (pos = m_threads.begin(); pos != end; ++pos) {
-    ThreadSP thread_sp(*pos);
-    if (thread_sp->GetResumeState() != eStateSuspended &&
-        (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) {
-      if (thread_sp->IsOperatingSystemPluginThread() &&
-          !thread_sp->GetBackingThread())
-        continue;
-      thread_sp->SetupForResume();
-    }
-  }
-
   // Now go through the threads and see if any thread wants to run just itself.
   // if so then pick one and run it.
 
@@ -595,6 +579,36 @@ bool ThreadList::WillResume() {
     }
   }
 
+  // Give all the threads that are likely to run a last chance to set up their
+  // state before we negotiate who is actually going to get a chance to run...
+  // Don't set to resume suspended threads, and if any thread wanted to stop
+  // others, only call setup on the threads that request StopOthers...
+  for (pos = m_threads.begin(); pos != end; ++pos) {
+    ThreadSP thread_sp(*pos);
+    if (thread_sp->GetResumeState() != eStateSuspended &&
+        (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) {
+      if (thread_sp->IsOperatingSystemPluginThread() &&
+          !thread_sp->GetBackingThread())
+        continue;
+      if (thread_sp->SetupForResume()) {
+        // You can't say "stop others" and also want yourself to be suspended.
+        assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
+        run_me_only_list.AddThread(thread_sp);
+
+        if (!(stop_others_thread_sp && stop_others_thread_sp->ShouldRunBeforePublicStop())) {
+          if (thread_sp == GetSelectedThread())
+            stop_others_thread_sp = thread_sp;
+
+          if (thread_sp->ShouldRunBeforePublicStop()) {
+            // This takes precedence, so if we find one of these, service it:
+            stop_others_thread_sp = thread_sp;
+            break;
+          }
+        }
+      }
+    }
+  }
+
   if (run_me_only_list.GetSize(false) == 0) {
     Log *log = GetLog(LLDBLog::Step);
     if (log && log->GetVerbose())

>From 8387ed904f9dc2c10b4db82a16390334a91e9a9d Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Wed, 18 Dec 2024 00:00:27 +0000
Subject: [PATCH 5/7] [lldb] Set `wants_solo_run` based on whether
 `run_me_only_list` is empty

We no longer need a dedicated loop to set `wants_solo_run`.
---
 lldb/source/Target/ThreadList.cpp | 32 ++++++++-----------------------
 1 file changed, 8 insertions(+), 24 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 8edbd13c64a8b8..70a496c4e2603b 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -518,30 +518,7 @@ bool ThreadList::WillResume() {
 
   collection::iterator pos, end = m_threads.end();
 
-  // See if any thread wants to run stopping others.  If it does, then we won't
-  // setup the other threads for resume, since they aren't going to get a
-  // chance to run.  This is necessary because the SetupForResume might add
-  // "StopOthers" plans which would then get to be part of the who-gets-to-run
-  // negotiation, but they're coming in after the fact, and the threads that
-  // are already set up should take priority.
-
-  bool wants_solo_run = false;
-
-  for (pos = m_threads.begin(); pos != end; ++pos) {
-    ThreadSP thread_sp(*pos);
-    lldbassert((*pos)->GetCurrentPlan() &&
-               "thread should not have null thread plan");
-    if (thread_sp->GetResumeState() != eStateSuspended &&
-        thread_sp->GetCurrentPlan()->StopOthers()) {
-      if (thread_sp->IsOperatingSystemPluginThread() &&
-          !thread_sp->GetBackingThread())
-        continue;
-      wants_solo_run = true;
-      break;
-    }
-  }
-
-  // Now go through the threads and see if any thread wants to run just itself.
+  // Go through the threads and see if any thread wants to run just itself.
   // if so then pick one and run it.
 
   ThreadList run_me_only_list(m_process);
@@ -583,8 +560,15 @@ bool ThreadList::WillResume() {
   // state before we negotiate who is actually going to get a chance to run...
   // Don't set to resume suspended threads, and if any thread wanted to stop
   // others, only call setup on the threads that request StopOthers...
+  bool wants_solo_run = run_me_only_list.GetSize(false) > 0;
   for (pos = m_threads.begin(); pos != end; ++pos) {
     ThreadSP thread_sp(*pos);
+    // See if any thread wants to run stopping others.  If it does, then we
+    // won't setup the other threads for resume, since they aren't going to get
+    // a chance to run.  This is necessary because the SetupForResume might add
+    // "StopOthers" plans which would then get to be part of the who-gets-to-run
+    // negotiation, but they're coming in after the fact, and the threads that
+    // are already set up should take priority.
     if (thread_sp->GetResumeState() != eStateSuspended &&
         (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) {
       if (thread_sp->IsOperatingSystemPluginThread() &&

>From b97eb65d41c6e20d046e241d161dfc46ec359154 Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Wed, 18 Dec 2024 01:16:50 +0000
Subject: [PATCH 6/7] [lldb] Move `thread_to_run` selection to happen before
 `SetupForResume()` is called

Later, we'll need to know which thread will run before we determine the
execution direction, which has to happen before we call `SetupForResume()`.
We fix up `thread_to_run` after calling `SetupForResume()`, if required.
---
 lldb/source/Target/ThreadList.cpp | 32 +++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 70a496c4e2603b..2c9e2a7a1d8b49 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -556,6 +556,20 @@ bool ThreadList::WillResume() {
     }
   }
 
+  ThreadSP thread_to_run;
+  if (run_me_only_list.GetSize(false) > 0) {
+    if (stop_others_thread_sp) {
+      thread_to_run = stop_others_thread_sp;
+    } else if (run_me_only_list.GetSize(false) == 1) {
+      thread_to_run = run_me_only_list.GetThreadAtIndex(0);
+    } else {
+      int random_thread =
+          (int)((run_me_only_list.GetSize(false) * (double)rand()) /
+                (RAND_MAX + 1.0));
+      thread_to_run = run_me_only_list.GetThreadAtIndex(random_thread);
+    }
+  }
+
   // Give all the threads that are likely to run a last chance to set up their
   // state before we negotiate who is actually going to get a chance to run...
   // Don't set to resume suspended threads, and if any thread wanted to stop
@@ -580,12 +594,15 @@ bool ThreadList::WillResume() {
         run_me_only_list.AddThread(thread_sp);
 
         if (!(stop_others_thread_sp && stop_others_thread_sp->ShouldRunBeforePublicStop())) {
-          if (thread_sp == GetSelectedThread())
+          if (thread_sp == GetSelectedThread()) {
             stop_others_thread_sp = thread_sp;
+            thread_to_run = thread_sp;
+          }
 
           if (thread_sp->ShouldRunBeforePublicStop()) {
             // This takes precedence, so if we find one of these, service it:
             stop_others_thread_sp = thread_sp;
+            thread_to_run = thread_sp;
             break;
           }
         }
@@ -622,19 +639,6 @@ bool ThreadList::WillResume() {
         need_to_resume = false;
     }
   } else {
-    ThreadSP thread_to_run;
-
-    if (stop_others_thread_sp) {
-      thread_to_run = stop_others_thread_sp;
-    } else if (run_me_only_list.GetSize(false) == 1) {
-      thread_to_run = run_me_only_list.GetThreadAtIndex(0);
-    } else {
-      int random_thread =
-          (int)((run_me_only_list.GetSize(false) * (double)rand()) /
-                (RAND_MAX + 1.0));
-      thread_to_run = run_me_only_list.GetThreadAtIndex(random_thread);
-    }
-
     for (pos = m_threads.begin(); pos != end; ++pos) {
       ThreadSP thread_sp(*pos);
       if (thread_sp == thread_to_run) {

>From e01ec74a8845367a402d6fc37a5a2b60ad1e0059 Mon Sep 17 00:00:00 2001
From: Robert O'Callahan <rocallahan at google.com>
Date: Wed, 18 Dec 2024 01:24:23 +0000
Subject: [PATCH 7/7] [lldb] Replace `stop_others_thread_sp` with
 `thread_to_run`

We don't need `stop_others_thread_sp` to be a separate variable anymore,
we can just use `thread_to_run` from the start.
---
 lldb/source/Target/ThreadList.cpp | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 2c9e2a7a1d8b49..b800d6e4aee6b9 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -531,8 +531,7 @@ bool ThreadList::WillResume() {
   // There are two special kinds of thread that have priority for "StopOthers":
   // a "ShouldRunBeforePublicStop thread, or the currently selected thread.  If
   // we find one satisfying that critereon, put it here.
-  ThreadSP stop_others_thread_sp;
-
+  ThreadSP thread_to_run;
   for (pos = m_threads.begin(); pos != end; ++pos) {
     ThreadSP thread_sp(*pos);
     if (thread_sp->GetResumeState() != eStateSuspended &&
@@ -546,21 +545,18 @@ bool ThreadList::WillResume() {
       run_me_only_list.AddThread(thread_sp);
 
       if (thread_sp == GetSelectedThread())
-        stop_others_thread_sp = thread_sp;
-        
+        thread_to_run = thread_sp;
+
       if (thread_sp->ShouldRunBeforePublicStop()) {
         // This takes precedence, so if we find one of these, service it:
-        stop_others_thread_sp = thread_sp;
+        thread_to_run = thread_sp;
         break;
       }
     }
   }
 
-  ThreadSP thread_to_run;
-  if (run_me_only_list.GetSize(false) > 0) {
-    if (stop_others_thread_sp) {
-      thread_to_run = stop_others_thread_sp;
-    } else if (run_me_only_list.GetSize(false) == 1) {
+  if (run_me_only_list.GetSize(false) > 0 && !thread_to_run) {
+    if (run_me_only_list.GetSize(false) == 1) {
       thread_to_run = run_me_only_list.GetThreadAtIndex(0);
     } else {
       int random_thread =
@@ -593,15 +589,12 @@ bool ThreadList::WillResume() {
         assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
         run_me_only_list.AddThread(thread_sp);
 
-        if (!(stop_others_thread_sp && stop_others_thread_sp->ShouldRunBeforePublicStop())) {
-          if (thread_sp == GetSelectedThread()) {
-            stop_others_thread_sp = thread_sp;
+        if (!(thread_to_run && thread_to_run->ShouldRunBeforePublicStop())) {
+          if (thread_sp == GetSelectedThread())
             thread_to_run = thread_sp;
-          }
 
           if (thread_sp->ShouldRunBeforePublicStop()) {
             // This takes precedence, so if we find one of these, service it:
-            stop_others_thread_sp = thread_sp;
             thread_to_run = thread_sp;
             break;
           }



More information about the lldb-commits mailing list