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.
This commit is contained in:
Markus Metzger
2024-02-21 14:53:59 +00:00
parent e20f632287
commit 57531a5fa4
3 changed files with 131 additions and 31 deletions

View 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,21 +2779,46 @@ 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;
}
}
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))
{
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
{
process_stratum_target *proc_target
= current_inferior ()->process_target ();
for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
{
tp->btrace.flags &= ~BTHR_MOVE;
tp->btrace.flags |= BTHR_STOP;
}
{
tp->btrace.flags &= ~BTHR_MOVE;
tp->btrace.flags |= BTHR_STOP;
}
}
}

View File

@@ -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 */
}

View 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"
}