From: Markus Metzger Date: Wed, 21 Feb 2024 14:53:59 +0000 (+0000) Subject: gdb, btrace: per-inferior run-control X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57531a5fa408243cb4443f64aa8380edc5cfccba;p=thirdparty%2Fbinutils-gdb.git 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. --- diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 42927f318b8..e300f132d0d 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 f2b8dbad670..eefd478c2bc 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 . */ +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 0dc866729c9..d499311ae32 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" }