From: Simon Marchi Date: Thu, 21 May 2020 22:42:16 +0000 (-0400) Subject: Handle multiple target events before commit resume X-Git-Url: http://git.efficios.com/?p=deliverable%2Fbinutils-gdb.git;a=commitdiff_plain;h=refs%2Fheads%2Fconcurrent-displaced-stepping-rocm-3.5 Handle multiple target events before commit resume Modify fetch_inferior_event to fetch (and handle) events from the target as long as it provides events. When resuming a thread that has hit a breakpoint (and it was decided that it should be resumed), don't commit_resume just yet. Wait until the target has no more events to provide to do so. Change-Id: Ia223a7d95dd5a4844c6a96e1125fb03d3e295584 --- diff --git a/gdb/infrun.c b/gdb/infrun.c index 4139e47139..f61e706ef9 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -91,7 +91,7 @@ static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR); static int maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc); -static void resume (gdb_signal sig); +static void resume (gdb_signal sig, bool commit = true); /* Asynchronous signal handler registered as event loop source for when we have pending events ready to be passed to the core. */ @@ -1876,7 +1876,8 @@ reset_ecs (struct execution_control_state *ecs, struct thread_info *tp) ecs->ptid = tp->ptid; } -static void keep_going_pass_signal (struct execution_control_state *ecs); +static void keep_going_pass_signal (struct execution_control_state *ecs, + bool commit = true); static void prepare_to_wait (struct execution_control_state *ecs); static int keep_going_stepped_thread (struct thread_info *tp); static step_over_what thread_still_needs_step_over (struct thread_info *tp); @@ -2179,7 +2180,8 @@ internal_resume_ptid (int user_step) bookkeeping. */ static void -do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig) +do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig, + bool commit) { struct thread_info *tp = inferior_thread (); @@ -2219,7 +2221,10 @@ do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig) target_resume (resume_ptid, step, sig); - target_commit_resume (); + if (commit) + target_commit_resume (); + else + current_top_target ()->pending_commit_resume = true; } /* Resume the inferior. SIG is the signal to give the inferior @@ -2227,7 +2232,7 @@ do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig) call 'resume', which handles exceptions. */ static void -resume_1 (enum gdb_signal sig) +resume_1 (enum gdb_signal sig, bool commit) { struct regcache *regcache = get_current_regcache (); struct gdbarch *gdbarch = regcache->arch (); @@ -2390,7 +2395,7 @@ resume_1 (enum gdb_signal sig) insert_breakpoints (); resume_ptid = internal_resume_ptid (user_step); - do_target_resume (resume_ptid, 0, GDB_SIGNAL_0); + do_target_resume (resume_ptid, 0, GDB_SIGNAL_0, commit); tp->resumed = 1; return; } @@ -2597,7 +2602,7 @@ resume_1 (enum gdb_signal sig) gdb_assert (pc_in_thread_step_range (pc, tp)); } - do_target_resume (resume_ptid, step, sig); + do_target_resume (resume_ptid, step, sig, commit); tp->resumed = 1; } @@ -2606,11 +2611,11 @@ resume_1 (enum gdb_signal sig) rolls back state on error. */ static void -resume (gdb_signal sig) +resume (gdb_signal sig, bool commit) { try { - resume_1 (sig); + resume_1 (sig, commit); } catch (const gdb_exception &ex) { @@ -3120,7 +3125,8 @@ static void check_exception_resume (struct execution_control_state *, static void end_stepping_range (struct execution_control_state *ecs); static void stop_waiting (struct execution_control_state *ecs); -static void keep_going (struct execution_control_state *ecs); +static void keep_going (struct execution_control_state *ecs, + bool commit = true); static void process_event_stop_test (struct execution_control_state *ecs); static int switch_back_to_stepped_thread (struct execution_control_state *ecs); @@ -3681,8 +3687,6 @@ fetch_inferior_event (void *client_data) int cmd_done = 0; ptid_t waiton_ptid = minus_one_ptid; - memset (ecs, 0, sizeof (*ecs)); - /* Events are always processed with the main UI as current UI. This way, warnings, debug output, etc. are always consistently sent to the main console. */ @@ -3723,78 +3727,92 @@ fetch_inferior_event (void *client_data) = make_scoped_restore (&execution_direction, target_execution_direction ()); - ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, - target_can_async_p () ? TARGET_WNOHANG : 0); - - if (debug_infrun) - print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws); + int iter = 0; + while (true) + { + memset (ecs, 0, sizeof (*ecs)); - /* If an error happens while handling the event, propagate GDB's - knowledge of the executing state to the frontend/user running - state. */ - ptid_t finish_ptid = !target_is_non_stop_p () ? minus_one_ptid : ecs->ptid; - scoped_finish_thread_state finish_state (finish_ptid); + ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, + target_can_async_p () ? TARGET_WNOHANG : 0); + if (ecs->ws.kind == TARGET_WAITKIND_IGNORE + || ecs->ws.kind == TARGET_WAITKIND_NO_RESUMED) + break; - /* Get executed before scoped_restore_current_thread above to apply - still for the thread which has thrown the exception. */ - auto defer_bpstat_clear - = make_scope_exit (bpstat_clear_actions); - auto defer_delete_threads - = make_scope_exit (delete_just_stopped_threads_infrun_breakpoints); + if (debug_infrun) + print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws); - /* Now figure out what to do with the result of the result. */ - handle_inferior_event (ecs); + /* If an error happens while handling the event, propagate GDB's + knowledge of the executing state to the frontend/user running + state. */ + ptid_t finish_ptid = !target_is_non_stop_p () ? minus_one_ptid : ecs->ptid; + scoped_finish_thread_state finish_state (finish_ptid); - if (!ecs->wait_some_more) - { - struct inferior *inf = find_inferior_ptid (ecs->ptid); - int should_stop = 1; - struct thread_info *thr = ecs->event_thread; + /* Get executed before scoped_restore_current_thread above to apply + still for the thread which has thrown the exception. */ + auto defer_bpstat_clear + = make_scope_exit (bpstat_clear_actions); + auto defer_delete_threads + = make_scope_exit (delete_just_stopped_threads_infrun_breakpoints); - delete_just_stopped_threads_infrun_breakpoints (); + /* Now figure out what to do with the result of the result. */ + handle_inferior_event (ecs); - if (thr != NULL) + if (!ecs->wait_some_more) { - struct thread_fsm *thread_fsm = thr->thread_fsm; + struct inferior *inf = find_inferior_ptid (ecs->ptid); + int should_stop = 1; + struct thread_info *thr = ecs->event_thread; - if (thread_fsm != NULL) - should_stop = thread_fsm->should_stop (thr); - } + delete_just_stopped_threads_infrun_breakpoints (); - if (!should_stop) - { - keep_going (ecs); - } - else - { - bool should_notify_stop = true; - int proceeded = 0; - - clean_up_just_stopped_threads_fsms (ecs); + if (thr != NULL) + { + struct thread_fsm *thread_fsm = thr->thread_fsm; - if (thr != NULL && thr->thread_fsm != NULL) - should_notify_stop = thr->thread_fsm->should_notify_stop (); + if (thread_fsm != NULL) + should_stop = thread_fsm->should_stop (thr); + } - if (should_notify_stop) + if (!should_stop) { - /* We may not find an inferior if this was a process exit. */ - if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY) - proceeded = normal_stop (); + keep_going (ecs); } - - if (!proceeded) + else { - inferior_event_handler (INF_EXEC_COMPLETE, NULL); - cmd_done = 1; + bool should_notify_stop = true; + int proceeded = 0; + + clean_up_just_stopped_threads_fsms (ecs); + + if (thr != NULL && thr->thread_fsm != NULL) + should_notify_stop = thr->thread_fsm->should_notify_stop (); + + if (should_notify_stop) + { + /* We may not find an inferior if this was a process exit. */ + if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY) + proceeded = normal_stop (); + } + + if (!proceeded) + { + inferior_event_handler (INF_EXEC_COMPLETE, NULL); + cmd_done = 1; + } } } - } - defer_delete_threads.release (); - defer_bpstat_clear.release (); + defer_delete_threads.release (); + defer_bpstat_clear.release (); - /* No error, don't finish the thread states yet. */ - finish_state.release (); + /* No error, don't finish the thread states yet. */ + finish_state.release (); + + iter++; + } + + if (current_top_target ()->pending_commit_resume) + current_top_target ()->commit_resume (); /* This scope is used to ensure that readline callbacks are reinstalled here. */ @@ -6244,7 +6262,7 @@ process_event_stop_test (struct execution_control_state *ecs) if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: no stepping, continue\n"); /* Likewise if we aren't even stepping. */ - keep_going (ecs); + keep_going (ecs, false /* don't commit */); return; } @@ -7022,7 +7040,7 @@ keep_going_stepped_thread (struct thread_info *tp) tp->resumed = 1; resume_ptid = internal_resume_ptid (tp->control.stepping_command); - do_target_resume (resume_ptid, 0, GDB_SIGNAL_0); + do_target_resume (resume_ptid, 0, GDB_SIGNAL_0, true); } else { @@ -7443,7 +7461,7 @@ stop_waiting (struct execution_control_state *ecs) signal is set to nopass. */ static void -keep_going_pass_signal (struct execution_control_state *ecs) +keep_going_pass_signal (struct execution_control_state *ecs, bool commit) { gdb_assert (ecs->event_thread->ptid == inferior_ptid); gdb_assert (!ecs->event_thread->resumed); @@ -7562,7 +7580,7 @@ keep_going_pass_signal (struct execution_control_state *ecs) ecs->event_thread->control.trap_expected = (remove_bp || remove_wps); - resume (ecs->event_thread->suspend.stop_signal); + resume (ecs->event_thread->suspend.stop_signal, commit); } prepare_to_wait (ecs); @@ -7573,7 +7591,7 @@ keep_going_pass_signal (struct execution_control_state *ecs) resuming part; waiting for the next event is done elsewhere. */ static void -keep_going (struct execution_control_state *ecs) +keep_going (struct execution_control_state *ecs, bool commit) { if (ecs->event_thread->control.trap_expected && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) @@ -7581,7 +7599,7 @@ keep_going (struct execution_control_state *ecs) if (!signal_program[ecs->event_thread->suspend.stop_signal]) ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; - keep_going_pass_signal (ecs); + keep_going_pass_signal (ecs, commit); } /* This function normally comes after a resume, before diff --git a/gdb/target.c b/gdb/target.c index e3e30afd7a..093f74759a 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -2123,8 +2123,12 @@ void target_commit_resume (void) { if (defer_target_commit_resume) - return; + { + current_top_target ()->pending_commit_resume = true; + return; + } + current_top_target ()->pending_commit_resume = false; current_top_target ()->commit_resume (); } diff --git a/gdb/target.h b/gdb/target.h index 7469e6bee7..1ce1ab8cc4 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -429,6 +429,8 @@ struct target_info struct target_ops { + bool pending_commit_resume = false; + /* Return this target's stratum. */ virtual strata stratum () const = 0;