aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2024-02-21 14:53:59 +0000
committerMarkus Metzger <markus.t.metzger@intel.com>2025-05-26 07:01:15 +0000
commit57531a5fa408243cb4443f64aa8380edc5cfccba (patch)
treecf3ae04a5ae025b359eb7ca2f6a4921e841b2ef2
parente20f63228799bd78293ff155244bd5009089aaf5 (diff)
downloadbinutils-57531a5fa408243cb4443f64aa8380edc5cfccba.zip
binutils-57531a5fa408243cb4443f64aa8380edc5cfccba.tar.gz
binutils-57531a5fa408243cb4443f64aa8380edc5cfccba.tar.bz2
gdb, btrace: per-inferior run-control
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.
-rw-r--r--gdb/record-btrace.c133
-rw-r--r--gdb/testsuite/gdb.btrace/multi-inferior.c10
-rw-r--r--gdb/testsuite/gdb.btrace/multi-inferior.exp19
3 files changed, 131 insertions, 31 deletions
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 42927f3..e300f13 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -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. */
diff --git a/gdb/testsuite/gdb.btrace/multi-inferior.c b/gdb/testsuite/gdb.btrace/multi-inferior.c
index f2b8dba..eefd478 100644
--- a/gdb/testsuite/gdb.btrace/multi-inferior.c
+++ b/gdb/testsuite/gdb.btrace/multi-inferior.c
@@ -15,8 +15,16 @@
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 */
}
diff --git a/gdb/testsuite/gdb.btrace/multi-inferior.exp b/gdb/testsuite/gdb.btrace/multi-inferior.exp
index 0dc8667..d499311 100644
--- a/gdb/testsuite/gdb.btrace/multi-inferior.exp
+++ b/gdb/testsuite/gdb.btrace/multi-inferior.exp
@@ -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"
}