]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb, btrace: per-inferior run-control
authorMarkus Metzger <markus.t.metzger@intel.com>
Wed, 21 Feb 2024 14:53:59 +0000 (14:53 +0000)
committerMarkus Metzger <markus.t.metzger@intel.com>
Mon, 26 May 2025 07:01:15 +0000 (07:01 +0000)
While recording is already per inferior, run-control isn't.  As soon as
any thread in any inferior is replaying, no other inferior can be resumed.

This is controlled by calls to record_is_replaying(minus_one_ptid).
Instead of minus_one_ptid, pass the ptid of the inferior to be checked,
and split requests for minus_one_ptid by inferior for resume and stop.

Since it is not safe to split a wait request for blocking targets, we
forward the minus_one_ptid request if there are no events to report for
replaying threads.

gdb/record-btrace.c
gdb/testsuite/gdb.btrace/multi-inferior.c
gdb/testsuite/gdb.btrace/multi-inferior.exp

index 42927f318b8dd150872f1f78197c5512777c5f11..e300f132d0d2477cc8fe3607e5bc5f754b19c875 100644 (file)
@@ -2191,11 +2191,14 @@ record_btrace_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
      For non-stop targets this means that no thread is replaying.  In order to
      make progress, we may need to explicitly move replaying threads to the end
      of their execution history.  */
-  if ((::execution_direction != EXEC_REVERSE)
-      && !record_is_replaying (minus_one_ptid))
+  if (::execution_direction != EXEC_REVERSE)
     {
-      this->beneath ()->resume (ptid, step, signal);
-      return;
+      ptid_t check { ptid == minus_one_ptid ? ptid : ptid_t (ptid.pid ()) };
+      if (!record_is_replaying (check))
+       {
+         this->beneath ()->resume (ptid, step, signal);
+         return;
+       }
     }
 
   /* Compute the btrace thread flag for the requested move.  */
@@ -2215,25 +2218,45 @@ record_btrace_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
 
      For all-stop targets, we only step INFERIOR_PTID and continue others.  */
 
-  process_stratum_target *proc_target = current_inferior ()->process_target ();
+  process_stratum_target *proc_target
+    = current_inferior ()->process_target ();
 
-  if (!target_is_non_stop_p ())
+  /* Split a minus_one_ptid request into per-inferior requests, so we can
+     forward them for inferiors that are not replaying.  */
+  if ((::execution_direction != EXEC_REVERSE) && (ptid == minus_one_ptid))
+    {
+      for (inferior *inf : all_non_exited_inferiors (proc_target))
+       {
+         ptid_t inf_ptid { inf->pid };
+         if (!record_is_replaying (inf_ptid))
+           {
+             this->beneath ()->resume (inf_ptid, step, signal);
+             continue;
+           }
+
+         for (thread_info *tp : inf->non_exited_threads ())
+           {
+             if (target_is_non_stop_p ()
+                 || tp->ptid.matches (inferior_ptid))
+               record_btrace_resume_thread (tp, flag);
+             else
+               record_btrace_resume_thread (tp, cflag);
+           }
+       }
+    }
+  else
     {
       gdb_assert (inferior_ptid.matches (ptid));
 
       for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
        {
-         if (tp->ptid.matches (inferior_ptid))
+         if (target_is_non_stop_p ()
+             || tp->ptid.matches (inferior_ptid))
            record_btrace_resume_thread (tp, flag);
          else
            record_btrace_resume_thread (tp, cflag);
        }
     }
-  else
-    {
-      for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
-       record_btrace_resume_thread (tp, flag);
-    }
 
   /* Async support.  */
   if (target_can_async_p ())
@@ -2610,10 +2633,11 @@ record_btrace_target::wait (ptid_t ptid, struct target_waitstatus *status,
         (unsigned) options);
 
   /* As long as we're not replaying, just forward the request.  */
-  if ((::execution_direction != EXEC_REVERSE)
-      && !record_is_replaying (minus_one_ptid))
+  if (::execution_direction != EXEC_REVERSE)
     {
-      return this->beneath ()->wait (ptid, status, options);
+      ptid_t check { ptid == minus_one_ptid ? ptid : ptid_t (ptid.pid ()) };
+      if (!record_is_replaying (check))
+       return this->beneath ()->wait (ptid, status, options);
     }
 
   /* Keep a work list of moving threads.  */
@@ -2624,6 +2648,19 @@ record_btrace_target::wait (ptid_t ptid, struct target_waitstatus *status,
 
   if (moving.empty ())
     {
+      /* Splitting a minus_one_ptid wait request per inferior is not safe
+        for blocking targets.  If one of the inferiors has an event to
+        report, but we happen to forward the wait request on another
+        inferior first that has nothing to report, we'd hang, whereas a
+        minus_one_ptid request would succeed.
+
+        A replaying inferior would be completely stopped for the target
+        beneath, so waiting for it should not result in any events.  It
+        should be safe to forward the minus_one_ptid request.  */
+      if ((::execution_direction != EXEC_REVERSE)
+         && (ptid == minus_one_ptid))
+       return this->beneath ()->wait (ptid, status, options);
+
       *status = btrace_step_no_moving_threads ();
 
       DEBUG ("wait ended by %s: %s", null_ptid.to_string ().c_str (),
@@ -2694,17 +2731,28 @@ record_btrace_target::wait (ptid_t ptid, struct target_waitstatus *status,
 
   gdb_assert (eventing != NULL);
 
-  /* We kept threads replaying at the end of their execution history.  Stop
-     replaying EVENTING now that we are going to report its stop.  */
-  record_btrace_stop_replaying_at_end (eventing);
-
   /* Stop all other threads. */
   if (!target_is_non_stop_p ())
     {
       for (thread_info *tp : current_inferior ()->non_exited_threads ())
-       record_btrace_cancel_resume (tp);
+       if (tp != eventing)
+         record_btrace_cancel_resume (tp);
+
+      if ((::execution_direction != EXEC_REVERSE) && (ptid == minus_one_ptid))
+       {
+         for (inferior *inf : all_non_exited_inferiors (proc_target))
+           {
+             ptid_t inf_ptid { inf->pid };
+             if (!record_is_replaying (inf_ptid))
+               this->beneath ()->stop (inf_ptid);
+           }
+       }
     }
 
+  /* We kept threads replaying at the end of their execution history.  Stop
+     replaying EVENTING now that we are going to report its stop.  */
+  record_btrace_stop_replaying_at_end (eventing);
+
   /* In async mode, we need to announce further events.  */
   if (target_is_async_p ())
     record_btrace_maybe_mark_async_event (moving, no_history);
@@ -2731,22 +2779,47 @@ record_btrace_target::stop (ptid_t ptid)
   DEBUG ("stop %s", ptid.to_string ().c_str ());
 
   /* As long as we're not replaying, just forward the request.  */
-  if ((::execution_direction != EXEC_REVERSE)
-      && !record_is_replaying (minus_one_ptid))
+  if (::execution_direction != EXEC_REVERSE)
     {
-      this->beneath ()->stop (ptid);
+      ptid_t check { ptid == minus_one_ptid ? ptid : ptid_t (ptid.pid ()) };
+      if (!record_is_replaying (check))
+       {
+         this->beneath ()->stop (ptid);
+         return;
+       }
     }
-  else
-    {
-      process_stratum_target *proc_target
-       = current_inferior ()->process_target ();
 
-      for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
+  process_stratum_target *proc_target
+    = current_inferior ()->process_target ();
+
+  /* Split a minus_one_ptid request into per-inferior requests, so we can
+     forward them for inferiors that are not replaying.  */
+  if ((::execution_direction != EXEC_REVERSE) && (ptid == minus_one_ptid))
+    {
+      for (inferior *inf : all_non_exited_inferiors (proc_target))
        {
-         tp->btrace.flags &= ~BTHR_MOVE;
-         tp->btrace.flags |= BTHR_STOP;
+         ptid_t inf_ptid { inf->pid };
+         if (!record_is_replaying (inf_ptid))
+           {
+             this->beneath ()->stop (inf_ptid);
+             continue;
+           }
+
+         for (thread_info *tp : inf->non_exited_threads ())
+           {
+             tp->btrace.flags &= ~BTHR_MOVE;
+             tp->btrace.flags |= BTHR_STOP;
+           }
        }
     }
+  else
+    {
+      for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
+      {
+       tp->btrace.flags &= ~BTHR_MOVE;
+       tp->btrace.flags |= BTHR_STOP;
+      }
+    }
  }
 
 /* The can_execute_reverse method of target record-btrace.  */
index f2b8dbad6701216527985b890359d73eef229e68..eefd478c2bc598ea469dc1fa4665f3052f616d09 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+static int
+fun (void)
+{
+  int x = fun (); /* fun.1 */
+  return x;       /* fun.2 */
+}
+
 int
 main (void)
 {
-  return 0;
+  int x = fun (); /* main.1 */
+  return x;       /* main.2 */
 }
index 0dc866729c9b66d99a0246cc59a8c6f2423671f5..d499311ae32e925643cbaefa2302f244f7349ef0 100644 (file)
@@ -39,6 +39,8 @@ with_test_prefix "inferior 1" {
     }
 
     gdb_test_no_output "record btrace"
+    gdb_test "step 4" "fun\.1.*"
+    gdb_test "reverse-step" "fun\.1.*"
 }
 
 with_test_prefix "inferior 2" {
@@ -51,4 +53,21 @@ with_test_prefix "inferior 2" {
     }
 
     gdb_test_no_output "record btrace"
+    gdb_test "step 4" "fun\.1.*"
+    gdb_test "reverse-step" "fun\.1.*"
+
+    gdb_test "info record" "Replay in progress.*"
+    gdb_test "record stop" "Process record is stopped.*"
+
+    gdb_test "step" "fun\.1.*"
+}
+
+with_test_prefix "inferior 1" {
+    gdb_test "inferior 1" "Switching to inferior 1.*"
+
+    gdb_test "info record" "Replay in progress.*"
+    gdb_test "reverse-finish" "fun\.1.*"
+    gdb_test "record goto end" "fun\.1.*"
+    gdb_test "step 2" "fun\.1.*"
+    gdb_test "reverse-step 3"
 }