aboutsummaryrefslogtreecommitdiff
path: root/gdb/infrun.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2014-05-29 12:27:01 +0100
committerPedro Alves <palves@redhat.com>2014-05-29 12:27:01 +0100
commit251bde03baf93dbb44d3785e09e03179916143e3 (patch)
treec4d4fd7b6df5cc684d7d4cbca655a6879237b221 /gdb/infrun.c
parent434415618f6bb9ac428a8d18ab33111920cd04dc (diff)
downloadgdb-251bde03baf93dbb44d3785e09e03179916143e3.zip
gdb-251bde03baf93dbb44d3785e09e03179916143e3.tar.gz
gdb-251bde03baf93dbb44d3785e09e03179916143e3.tar.bz2
PR15693 - Fix spurious *running events, thread state, dprintf-style call
If one sets a breakpoint with a condition that involves calling a function in the inferior, and then the condition evaluates false, GDB outputs one *running event for each time the program hits the breakpoint. E.g., $ gdb return-false -i=mi (gdb) start ... (gdb) b 14 if return_false () &"b 14 if return_false ()\n" ~"Breakpoint 2 at 0x4004eb: file return-false.c, line 14.\n" ... ^done (gdb) c &"c\n" ~"Continuing.\n" ^running *running,thread-id=(...) (gdb) *running,thread-id=(...) *running,thread-id=(...) *running,thread-id=(...) *running,thread-id=(...) *running,thread-id=(...) ... repeat forever ... An easy way a user can trip on this is with a dprintf with "set dprintf-style call". In that case, a dprintf is just a breakpoint that when hit GDB calls the printf function in the inferior, and then resumes it, just like the case above. If the breakpoint/dprintf is set in a loop, then these spurious events can potentially slow down a frontend much, if it decides to refresh its GUI whenever it sees this event (Eclipse is one such case). When we run an infcall, we pretend we don't actually run the inferior. This is already handled for the usual case of calling a function directly from the CLI: (gdb) p return_false () &"p return_false ()\n" ~"$1 = 0" ~"\n" ^done (gdb) Note no *running, nor *stopped events. That's handled by: static void mi_on_resume (ptid_t ptid) { ... /* Suppress output while calling an inferior function. */ if (tp->control.in_infcall) return; and equivalent code on normal_stop. However, in the cases of the PR, after finishing the infcall there's one more resume, and mi_on_resume doesn't know that it should suppress output then too, somehow. The "running/stopped" state is a high level user/frontend state. Internal stops are invisible to the frontend. If follows from that that we should be setting the thread to running at a higher level where we still know the set of threads the user _intends_ to resume. Currently we mark a thread as running from within target_resume, a low level target operation. As consequence, today, if we resume a multi-threaded program while stopped at a breakpoint, we see this: -exec-continue ^running *running,thread-id="1" (gdb) *running,thread-id="all" The first *running was GDB stepping over the breakpoint, and the second is GDB finally resuming everything. Between those two *running's, threads other than "1" still have their state set to stopped. That's bogus -- in async mode, this opens a tiny window between both resumes where the user might try to run another execution command to threads other than thread 1, and very much confuse GDB. That is, the "step" below should fail the "step", complaining that the thread is running: (gdb) c -a & (gdb) thread 2 (gdb) step IOW, threads that GDB happens to not resume immediately (say, because it needs to step over a breakpoint) shall still be marked as running. Then, if we move marking threads as running to a higher layer, decoupled from target_resume, plus skip marking threads as running when running an infcall, the spurious *running events disappear, because there will be no state transitions at all. I think we might end up adding a new thread state -- THREAD_INFCALL or some such, however since infcalls are always synchronous today, I didn't find a need. There's no way to execute a CLI/MI command directly from the prompt if some thread is running an infcall. Tested on x86_64 Fedora 20. gdb/ 2014-05-29 Pedro Alves <palves@redhat.com> PR PR15693 * infrun.c (resume): Determine how much to resume depending on whether the caller wanted a step, not whether we can hardware step the target. Mark all threads that we intend to run as running, unless we're calling an inferior function. (normal_stop): If the thread is running an infcall, don't finish thread state. * target.c (target_resume): Don't mark threads as running here. gdb/testsuite/ 2014-05-29 Pedro Alves <palves@redhat.com> Hui Zhu <hui@codesourcery.com> PR PR15693 * gdb.mi/mi-condbreak-call-thr-state-mt.c: New file. * gdb.mi/mi-condbreak-call-thr-state-st.c: New file. * gdb.mi/mi-condbreak-call-thr-state.c: New file. * gdb.mi/mi-condbreak-call-thr-state.exp: New file.
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r--gdb/infrun.c44
1 files changed, 33 insertions, 11 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c
index ec78148..2068a2a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1780,6 +1780,7 @@ resume (int step, enum gdb_signal sig)
CORE_ADDR pc = regcache_read_pc (regcache);
struct address_space *aspace = get_regcache_aspace (regcache);
ptid_t resume_ptid;
+ int hw_step = step;
QUIT;
@@ -1799,7 +1800,7 @@ resume (int step, enum gdb_signal sig)
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: resume : clear step\n");
- step = 0;
+ hw_step = 0;
}
if (debug_infrun)
@@ -1844,7 +1845,7 @@ a command like `return' or `jump' to continue execution."));
step software breakpoint. */
if (use_displaced_stepping (gdbarch)
&& (tp->control.trap_expected
- || (step && gdbarch_software_single_step_p (gdbarch)))
+ || (hw_step && gdbarch_software_single_step_p (gdbarch)))
&& sig == GDB_SIGNAL_0
&& !current_inferior ()->waiting_for_vfork_done)
{
@@ -1854,11 +1855,14 @@ a command like `return' or `jump' to continue execution."));
{
/* Got placed in displaced stepping queue. Will be resumed
later when all the currently queued displaced stepping
- requests finish. The thread is not executing at this point,
- and the call to set_executing will be made later. But we
- need to call set_running here, since from frontend point of view,
- the thread is running. */
- set_running (inferior_ptid, 1);
+ requests finish. The thread is not executing at this
+ point, and the call to set_executing will be made later.
+ But we need to call set_running here, since from the
+ user/frontend's point of view, threads were set running.
+ Unless we're calling an inferior function, as in that
+ case we pretend the inferior doesn't run at all. */
+ if (!tp->control.in_infcall)
+ set_running (user_visible_resume_ptid (step), 1);
discard_cleanups (old_cleanups);
return;
}
@@ -1868,8 +1872,8 @@ a command like `return' or `jump' to continue execution."));
pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
displaced = get_displaced_stepping_state (ptid_get_pid (inferior_ptid));
- step = gdbarch_displaced_step_hw_singlestep (gdbarch,
- displaced->step_closure);
+ hw_step = gdbarch_displaced_step_hw_singlestep (gdbarch,
+ displaced->step_closure);
}
/* Do we need to do it the hard way, w/temp breakpoints? */
@@ -1933,6 +1937,14 @@ a command like `return' or `jump' to continue execution."));
by applying increasingly restricting conditions. */
resume_ptid = user_visible_resume_ptid (step);
+ /* Even if RESUME_PTID is a wildcard, and we end up resuming less
+ (e.g., we might need to step over a breakpoint), from the
+ user/frontend's point of view, all threads in RESUME_PTID are now
+ running. Unless we're calling an inferior function, as in that
+ case pretend we inferior doesn't run at all. */
+ if (!tp->control.in_infcall)
+ set_running (resume_ptid, 1);
+
/* Maybe resume a single thread after all. */
if ((step || singlestep_breakpoints_inserted_p)
&& tp->control.trap_expected)
@@ -6188,8 +6200,18 @@ normal_stop (void)
if (has_stack_frames () && !stop_stack_dummy)
set_current_sal_from_frame (get_current_frame ());
- /* Let the user/frontend see the threads as stopped. */
- do_cleanups (old_chain);
+ /* Let the user/frontend see the threads as stopped, but do nothing
+ if the thread was running an infcall. We may be e.g., evaluating
+ a breakpoint condition. In that case, the thread had state
+ THREAD_RUNNING before the infcall, and shall remain set to
+ running, all without informing the user/frontend about state
+ transition changes. If this is actually a call command, then the
+ thread was originally already stopped, so there's no state to
+ finish either. */
+ if (target_has_execution && inferior_thread ()->control.in_infcall)
+ discard_cleanups (old_chain);
+ else
+ do_cleanups (old_chain);
/* Look up the hook_stop and run it (CLI internally handles problem
of stop_command's pre-hook not existing). */