struct thread_info *th, *tmp;
struct inferior *inf = current_inferior ();
int pid = ptid_get_pid (ptid);
+ ptid_t process_ptid;
/* This is an exec event that we actually wish to pay attention to.
Refresh our symbol table to the newly exec'd program, remove any
update_breakpoints_after_exec ();
/* What is this a.out's name? */
+ process_ptid = pid_to_ptid (pid);
printf_unfiltered (_("%s is executing new program: %s\n"),
- target_pid_to_str (inferior_ptid),
+ target_pid_to_str (process_ptid),
execd_pathname);
/* We've followed the inferior through an exec. Therefore, the
if (follow_exec_mode_string == follow_exec_mode_new)
{
- struct program_space *pspace;
-
/* The user wants to keep the old inferior and program spaces
around. Create a new fresh one, and switch to it. */
the same ptid, which can confuse find_inferior_ptid. */
exit_inferior_num_silent (current_inferior ()->num);
- inf = add_inferior (pid);
- pspace = add_program_space (maybe_new_address_space ());
- inf->pspace = pspace;
- inf->aspace = pspace->aspace;
- add_thread (ptid);
+ inf = add_inferior_with_spaces ();
+ inf->pid = pid;
+ target_follow_exec (inf, execd_pathname);
set_current_inferior (inf);
- set_current_program_space (pspace);
+ set_current_program_space (inf->pspace);
+ add_thread (ptid);
}
else
{
\f
/* Proceeding. */
+/* See infrun.h. */
+
+/* Counter that tracks number of user visible stops. This can be used
+ to tell whether a command has proceeded the inferior past the
+ current location. This allows e.g., inferior function calls in
+ breakpoint commands to not interrupt the command list. When the
+ call finishes successfully, the inferior is standing at the same
+ breakpoint as if nothing happened (and so we don't call
+ normal_stop). */
+static ULONGEST current_stop_id;
+
+/* See infrun.h. */
+
+ULONGEST
+get_stop_id (void)
+{
+ return current_stop_id;
+}
+
+/* Called when we report a user visible stop. */
+
+static void
+new_stop_id (void)
+{
+ current_stop_id++;
+}
+
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
if (!ecs->wait_some_more)
{
- struct thread_info *tp;
-
/* Cancel any running execution command. */
thread_cancel_execution_command (info);
normal_stop ();
-
- /* Finish off the continuations. */
- tp = inferior_thread ();
- do_all_intermediate_continuations_thread (tp, 1);
- do_all_continuations_thread (tp, 1);
}
do_cleanups (old_chain);
struct inferior *inf = find_inferior_ptid (ecs->ptid);
int should_stop = 1;
struct thread_info *thr = ecs->event_thread;
+ int should_notify_stop = 1;
delete_just_stopped_threads_infrun_breakpoints ();
{
clean_up_just_stopped_threads_fsms (ecs);
- /* We may not find an inferior if this was a process exit. */
- if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
- normal_stop ();
+ if (thr != NULL && thr->thread_fsm != NULL)
+ {
+ should_notify_stop
+ = thread_fsm_should_notify_stop (thr->thread_fsm);
+ }
+
+ if (should_notify_stop)
+ {
+ int proceeded = 0;
+
+ /* 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 ();
- inferior_event_handler (INF_EXEC_COMPLETE, NULL);
- cmd_done = 1;
+ if (!proceeded)
+ {
+ inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+ cmd_done = 1;
+ }
+ }
}
}
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n");
/* Reverse execution: target ran out of history info. */
+ /* Switch to the stopped thread. */
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ context_switch (ecs->ptid);
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n");
+
delete_just_stopped_threads_single_step_breakpoints ();
- stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+ stop_pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
observer_notify_no_history ();
stop_waiting (ecs);
return;
}
}
-/* Here to return control to GDB when the inferior stops for real.
- Print appropriate messages, remove breakpoints, give terminal our modes.
-
- STOP_PRINT_FRAME nonzero means print the executing frame
- (pc, function, args, file, line number and line text).
- BREAKPOINTS_FAILED nonzero means stop was due to error
- attempting to insert breakpoints. */
+/* See infrun.h. */
void
+maybe_remove_breakpoints (void)
+{
+ if (!breakpoints_should_be_inserted_now () && target_has_execution)
+ {
+ if (remove_breakpoints ())
+ {
+ target_terminal_ours_for_output ();
+ printf_filtered (_("Cannot remove breakpoints because "
+ "program is no longer writable.\nFurther "
+ "execution is probably impossible.\n"));
+ }
+ }
+}
+
+/* The execution context that just caused a normal stop. */
+
+struct stop_context
+{
+ /* The stop ID. */
+ ULONGEST stop_id;
+
+ /* The event PTID. */
+
+ ptid_t ptid;
+
+ /* If stopp for a thread event, this is the thread that caused the
+ stop. */
+ struct thread_info *thread;
+
+ /* The inferior that caused the stop. */
+ int inf_num;
+};
+
+/* Returns a new stop context. If stopped for a thread event, this
+ takes a strong reference to the thread. */
+
+static struct stop_context *
+save_stop_context (void)
+{
+ struct stop_context *sc = xmalloc (sizeof (struct stop_context));
+
+ sc->stop_id = get_stop_id ();
+ sc->ptid = inferior_ptid;
+ sc->inf_num = current_inferior ()->num;
+
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ {
+ /* Take a strong reference so that the thread can't be deleted
+ yet. */
+ sc->thread = inferior_thread ();
+ sc->thread->refcount++;
+ }
+ else
+ sc->thread = NULL;
+
+ return sc;
+}
+
+/* Release a stop context previously created with save_stop_context.
+ Releases the strong reference to the thread as well. */
+
+static void
+release_stop_context_cleanup (void *arg)
+{
+ struct stop_context *sc = arg;
+
+ if (sc->thread != NULL)
+ sc->thread->refcount--;
+ xfree (sc);
+}
+
+/* Return true if the current context no longer matches the saved stop
+ context. */
+
+static int
+stop_context_changed (struct stop_context *prev)
+{
+ if (!ptid_equal (prev->ptid, inferior_ptid))
+ return 1;
+ if (prev->inf_num != current_inferior ()->num)
+ return 1;
+ if (prev->thread != NULL && prev->thread->state != THREAD_STOPPED)
+ return 1;
+ if (get_stop_id () != prev->stop_id)
+ return 1;
+ return 0;
+}
+
+/* See infrun.h. */
+
+int
normal_stop (void)
{
struct target_waitstatus last;
get_last_target_status (&last_ptid, &last);
+ new_stop_id ();
+
/* If an exception is thrown from this point on, make sure to
propagate GDB's knowledge of the executing state to the
frontend/user running state. A QUIT is an easy exception to see
}
/* Note: this depends on the update_thread_list call above. */
- if (!breakpoints_should_be_inserted_now () && target_has_execution)
- {
- if (remove_breakpoints ())
- {
- target_terminal_ours_for_output ();
- printf_filtered (_("Cannot remove breakpoints because "
- "program is no longer writable.\nFurther "
- "execution is probably impossible.\n"));
- }
- }
+ maybe_remove_breakpoints ();
/* If an auto-display called a function and that got a signal,
delete that auto-display to avoid an infinite recursion. */
target_terminal_ours ();
async_enable_stdin ();
- /* Set the current source location. This will also happen if we
- display the frame below, but the current SAL will be incorrect
- during a user hook-stop function. */
- if (has_stack_frames () && !stop_stack_dummy)
- set_current_sal_from_frame (get_current_frame ());
-
- /* Let the user/frontend see the threads as stopped, but defer to
- call_function_by_hand if the thread finished an infcall
- successfully. We may be e.g., evaluating a breakpoint condition.
- In that case, the thread had state THREAD_RUNNING before the
- infcall, and shall remain marked running, all without informing
- the user/frontend about state transition changes. */
- if (target_has_execution
- && inferior_thread ()->control.in_infcall
- && stop_stack_dummy == STOP_STACK_DUMMY)
- 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). */
- if (stop_command)
- catch_errors (hook_stop_stub, stop_command,
- "Error while running hook_stop:\n", RETURN_MASK_ALL);
+ /* Let the user/frontend see the threads as stopped. */
+ do_cleanups (old_chain);
- if (!has_stack_frames ())
- goto done;
+ /* Select innermost stack frame - i.e., current frame is frame 0,
+ and current location is based on that. Handle the case where the
+ dummy call is returning after being stopped. E.g. the dummy call
+ previously hit a breakpoint. (If the dummy call returns
+ normally, we won't reach here.) Do this before the stop hook is
+ run, so that it doesn't get to see the temporary dummy frame,
+ which is not where we'll present the stop. */
+ if (has_stack_frames ())
+ {
+ if (stop_stack_dummy == STOP_STACK_DUMMY)
+ {
+ /* Pop the empty frame that contains the stack dummy. This
+ also restores inferior state prior to the call (struct
+ infcall_suspend_state). */
+ struct frame_info *frame = get_current_frame ();
- if (last.kind == TARGET_WAITKIND_SIGNALLED
- || last.kind == TARGET_WAITKIND_EXITED)
- goto done;
+ gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
+ frame_pop (frame);
+ /* frame_pop calls reinit_frame_cache as the last thing it
+ does which means there's now no selected frame. */
+ }
- /* Select innermost stack frame - i.e., current frame is frame 0,
- and current location is based on that.
- Don't do this on return from a stack dummy routine,
- or if the program has exited. */
-
- if (!stop_stack_dummy)
- select_frame (get_current_frame ());
-
- if (stop_stack_dummy == STOP_STACK_DUMMY)
- {
- /* Pop the empty frame that contains the stack dummy.
- This also restores inferior state prior to the call
- (struct infcall_suspend_state). */
- struct frame_info *frame = get_current_frame ();
-
- gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
- frame_pop (frame);
- /* frame_pop() calls reinit_frame_cache as the last thing it
- does which means there's currently no selected frame. We
- don't need to re-establish a selected frame if the dummy call
- returns normally, that will be done by
- restore_infcall_control_state. However, we do have to handle
- the case where the dummy call is returning after being
- stopped (e.g. the dummy call previously hit a breakpoint).
- We can't know which case we have so just always re-establish
- a selected frame here. */
select_frame (get_current_frame ());
- }
-done:
+ /* Set the current source location. */
+ set_current_sal_from_frame (get_current_frame ());
+ }
- /* Suppress the stop observer if we're in the middle of:
+ /* Look up the hook_stop and run it (CLI internally handles problem
+ of stop_command's pre-hook not existing). */
+ if (stop_command != NULL)
+ {
+ struct stop_context *saved_context = save_stop_context ();
+ struct cleanup *old_chain
+ = make_cleanup (release_stop_context_cleanup, saved_context);
- - calling an inferior function, as we pretend we inferior didn't
- run at all. The return value of the call is handled by the
- expression evaluator, through call_function_by_hand. */
+ catch_errors (hook_stop_stub, stop_command,
+ "Error while running hook_stop:\n", RETURN_MASK_ALL);
- if (!target_has_execution
- || last.kind == TARGET_WAITKIND_SIGNALLED
- || last.kind == TARGET_WAITKIND_EXITED
- || last.kind == TARGET_WAITKIND_NO_RESUMED
- || !inferior_thread ()->control.in_infcall)
- {
- if (!ptid_equal (inferior_ptid, null_ptid))
- observer_notify_normal_stop (inferior_thread ()->control.stop_bpstat,
- stop_print_frame);
- else
- observer_notify_normal_stop (NULL, stop_print_frame);
+ /* If the stop hook resumes the target, then there's no point in
+ trying to notify about the previous stop; its context is
+ gone. Likewise if the command switches thread or inferior --
+ the observers would print a stop for the wrong
+ thread/inferior. */
+ if (stop_context_changed (saved_context))
+ {
+ do_cleanups (old_chain);
+ return 1;
+ }
+ do_cleanups (old_chain);
}
+ /* Notify observers about the stop. This is where the interpreters
+ print the stop event. */
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ observer_notify_normal_stop (inferior_thread ()->control.stop_bpstat,
+ stop_print_frame);
+ else
+ observer_notify_normal_stop (NULL, stop_print_frame);
+
annotate_stopped ();
if (target_has_execution)
longer needed. Keeping those around slows down things linearly.
Note that this never removes the current inferior. */
prune_inferiors ();
+
+ return 0;
}
static int