Convert infcalls to thread_fsm mechanism
This removes infcall-specific special casing from normal_stop, simplifying it. Like the "finish" command's, the FSM is responsible for storing the function's return value. gdb/ChangeLog: 2015-09-09 Pedro Alves <palves@redhat.com> * infcall.c: Include thread_fsm.h. (struct call_return_meta_info): New. (get_call_return_value): New function, factored out from call_function_by_hand_dummy. (struct call_thread_fsm): New. (call_thread_fsm_ops): New global. (new_call_thread_fsm, call_thread_fsm_should_stop) (call_thread_fsm_should_notify_stop): New functions. (run_inferior_call): Add 'sm' parameter. Associate the FSM with the thread. (call_function_by_hand_dummy): Create a new call_thread_fsm instance, associate it with the thread, and wait for the FSM to finish. If finished successfully, fetch the function's result value out of the FSM. * infrun.c (fetch_inferior_event): If the FSM says the stop shouldn't be notified, don't call normal_stop. (maybe_remove_breakpoints): New function, factored out from ... (normal_stop): ... here. Simplify. * infrun.h (maybe_remove_breakpoints): Declare. * thread-fsm.c (thread_fsm_should_notify_stop): New function. (thread-fsm.h) <struct thread_fsm_ops>: New field. (thread_fsm_should_notify_stop): Declare.
This commit is contained in:
parent
243a925328
commit
388a708404
6 changed files with 348 additions and 159 deletions
|
@ -1,3 +1,28 @@
|
|||
2015-09-09 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* infcall.c: Include thread_fsm.h.
|
||||
(struct call_return_meta_info): New.
|
||||
(get_call_return_value): New function, factored out from
|
||||
call_function_by_hand_dummy.
|
||||
(struct call_thread_fsm): New.
|
||||
(call_thread_fsm_ops): New global.
|
||||
(new_call_thread_fsm, call_thread_fsm_should_stop)
|
||||
(call_thread_fsm_should_notify_stop): New functions.
|
||||
(run_inferior_call): Add 'sm' parameter. Associate the FSM with
|
||||
the thread.
|
||||
(call_function_by_hand_dummy): Create a new call_thread_fsm
|
||||
instance, associate it with the thread, and wait for the FSM to
|
||||
finish. If finished successfully, fetch the function's result
|
||||
value out of the FSM.
|
||||
* infrun.c (fetch_inferior_event): If the FSM says the stop
|
||||
shouldn't be notified, don't call normal_stop.
|
||||
(maybe_remove_breakpoints): New function, factored out from ...
|
||||
(normal_stop): ... here. Simplify.
|
||||
* infrun.h (maybe_remove_breakpoints): Declare.
|
||||
* thread-fsm.c (thread_fsm_should_notify_stop): New function.
|
||||
(thread-fsm.h) <struct thread_fsm_ops>: New field.
|
||||
(thread_fsm_should_notify_stop): Declare.
|
||||
|
||||
2015-09-09 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* Makefile.in (COMMON_OBS): Add thread-fsm.o.
|
||||
|
|
305
gdb/infcall.c
305
gdb/infcall.c
|
@ -38,6 +38,7 @@
|
|||
#include "observer.h"
|
||||
#include "top.h"
|
||||
#include "interps.h"
|
||||
#include "thread-fsm.h"
|
||||
|
||||
/* If we can't find a function's name from its address,
|
||||
we print this instead. */
|
||||
|
@ -374,6 +375,174 @@ get_function_name (CORE_ADDR funaddr, char *buf, int buf_size)
|
|||
}
|
||||
}
|
||||
|
||||
/* All the meta data necessary to extract the call's return value. */
|
||||
|
||||
struct call_return_meta_info
|
||||
{
|
||||
/* The caller frame's architecture. */
|
||||
struct gdbarch *gdbarch;
|
||||
|
||||
/* The called function. */
|
||||
struct value *function;
|
||||
|
||||
/* The return value's type. */
|
||||
struct type *value_type;
|
||||
|
||||
/* Are we returning a value using a structure return or a normal
|
||||
value return? */
|
||||
int struct_return_p;
|
||||
|
||||
/* If using a structure return, this is the structure's address. */
|
||||
CORE_ADDR struct_addr;
|
||||
|
||||
/* Whether stack temporaries are enabled. */
|
||||
int stack_temporaries_enabled;
|
||||
};
|
||||
|
||||
/* Extract the called function's return value. */
|
||||
|
||||
static struct value *
|
||||
get_call_return_value (struct call_return_meta_info *ri)
|
||||
{
|
||||
struct value *retval = NULL;
|
||||
int stack_temporaries = thread_stack_temporaries_enabled_p (inferior_ptid);
|
||||
|
||||
if (TYPE_CODE (ri->value_type) == TYPE_CODE_VOID)
|
||||
retval = allocate_value (ri->value_type);
|
||||
else if (ri->struct_return_p)
|
||||
{
|
||||
if (stack_temporaries)
|
||||
{
|
||||
retval = value_from_contents_and_address (ri->value_type, NULL,
|
||||
ri->struct_addr);
|
||||
push_thread_stack_temporary (inferior_ptid, retval);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = allocate_value (ri->value_type);
|
||||
read_value_memory (retval, 0, 1, ri->struct_addr,
|
||||
value_contents_raw (retval),
|
||||
TYPE_LENGTH (ri->value_type));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = allocate_value (ri->value_type);
|
||||
gdbarch_return_value (ri->gdbarch, ri->function, ri->value_type,
|
||||
get_current_regcache (),
|
||||
value_contents_raw (retval), NULL);
|
||||
if (stack_temporaries && class_or_union_p (ri->value_type))
|
||||
{
|
||||
/* Values of class type returned in registers are copied onto
|
||||
the stack and their lval_type set to lval_memory. This is
|
||||
required because further evaluation of the expression
|
||||
could potentially invoke methods on the return value
|
||||
requiring GDB to evaluate the "this" pointer. To evaluate
|
||||
the this pointer, GDB needs the memory address of the
|
||||
value. */
|
||||
value_force_lval (retval, ri->struct_addr);
|
||||
push_thread_stack_temporary (inferior_ptid, retval);
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (retval != NULL);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Data for the FSM that manages an infcall. It's main job is to
|
||||
record the called function's return value. */
|
||||
|
||||
struct call_thread_fsm
|
||||
{
|
||||
/* The base class. */
|
||||
struct thread_fsm thread_fsm;
|
||||
|
||||
/* All the info necessary to be able to extract the return
|
||||
value. */
|
||||
struct call_return_meta_info return_meta_info;
|
||||
|
||||
/* The called function's return value. This is extracted from the
|
||||
target before the dummy frame is popped. */
|
||||
struct value *return_value;
|
||||
};
|
||||
|
||||
static int call_thread_fsm_should_stop (struct thread_fsm *self);
|
||||
static int call_thread_fsm_should_notify_stop (struct thread_fsm *self);
|
||||
|
||||
/* call_thread_fsm's vtable. */
|
||||
|
||||
static struct thread_fsm_ops call_thread_fsm_ops =
|
||||
{
|
||||
NULL, /*dtor */
|
||||
NULL, /* clean_up */
|
||||
call_thread_fsm_should_stop,
|
||||
NULL, /* return_value */
|
||||
NULL, /* async_reply_reason*/
|
||||
call_thread_fsm_should_notify_stop,
|
||||
};
|
||||
|
||||
/* Allocate a new call_thread_fsm object. */
|
||||
|
||||
static struct call_thread_fsm *
|
||||
new_call_thread_fsm (struct gdbarch *gdbarch, struct value *function,
|
||||
struct type *value_type,
|
||||
int struct_return_p, CORE_ADDR struct_addr)
|
||||
{
|
||||
struct call_thread_fsm *sm;
|
||||
|
||||
sm = XCNEW (struct call_thread_fsm);
|
||||
thread_fsm_ctor (&sm->thread_fsm, &call_thread_fsm_ops);
|
||||
|
||||
sm->return_meta_info.gdbarch = gdbarch;
|
||||
sm->return_meta_info.function = function;
|
||||
sm->return_meta_info.value_type = value_type;
|
||||
sm->return_meta_info.struct_return_p = struct_return_p;
|
||||
sm->return_meta_info.struct_addr = struct_addr;
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
/* Implementation of should_stop method for infcalls. */
|
||||
|
||||
static int
|
||||
call_thread_fsm_should_stop (struct thread_fsm *self)
|
||||
{
|
||||
struct call_thread_fsm *f = (struct call_thread_fsm *) self;
|
||||
|
||||
if (stop_stack_dummy == STOP_STACK_DUMMY)
|
||||
{
|
||||
/* Done. */
|
||||
thread_fsm_set_finished (self);
|
||||
|
||||
/* Stash the return value before the dummy frame is popped and
|
||||
registers are restored to what they were before the
|
||||
call.. */
|
||||
f->return_value = get_call_return_value (&f->return_meta_info);
|
||||
|
||||
/* Break out of wait_sync_command_done. */
|
||||
async_enable_stdin ();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Implementation of should_notify_stop method for infcalls. */
|
||||
|
||||
static int
|
||||
call_thread_fsm_should_notify_stop (struct thread_fsm *self)
|
||||
{
|
||||
if (thread_fsm_finished_p (self))
|
||||
{
|
||||
/* Infcall succeeded. Be silent and proceed with evaluating the
|
||||
expression. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Something wrong happened. E.g., an unexpected breakpoint
|
||||
triggered, or a signal was intercepted. Notify the stop. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Subroutine of call_function_by_hand to simplify it.
|
||||
Start up the inferior and wait for it to stop.
|
||||
Return the exception if there's an error, or an exception with
|
||||
|
@ -383,7 +552,8 @@ get_function_name (CORE_ADDR funaddr, char *buf, int buf_size)
|
|||
thrown errors. The caller should rethrow if there's an error. */
|
||||
|
||||
static struct gdb_exception
|
||||
run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
|
||||
run_inferior_call (struct call_thread_fsm *sm,
|
||||
struct thread_info *call_thread, CORE_ADDR real_pc)
|
||||
{
|
||||
struct gdb_exception caught_error = exception_none;
|
||||
int saved_in_infcall = call_thread->control.in_infcall;
|
||||
|
@ -402,6 +572,11 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
|
|||
|
||||
clear_proceed_status (0);
|
||||
|
||||
/* Associate the FSM with the thread after clear_proceed_status
|
||||
(otherwise it'd clear this FSM), and before anything throws, so
|
||||
we don't leak it (and any resources it manages). */
|
||||
call_thread->thread_fsm = &sm->thread_fsm;
|
||||
|
||||
disable_watchpoints_before_interactive_call_start ();
|
||||
|
||||
/* We want to print return value, please... */
|
||||
|
@ -620,8 +795,6 @@ call_function_by_hand_dummy (struct value *function,
|
|||
struct gdb_exception e;
|
||||
char name_buf[RAW_FUNCTION_ADDRESS_SIZE];
|
||||
int stack_temporaries = thread_stack_temporaries_enabled_p (inferior_ptid);
|
||||
struct dummy_frame_context_saver *context_saver;
|
||||
struct cleanup *context_saver_cleanup;
|
||||
|
||||
if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
|
||||
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
|
||||
|
@ -1008,13 +1181,6 @@ call_function_by_hand_dummy (struct value *function,
|
|||
register_dummy_frame_dtor (dummy_id, inferior_ptid,
|
||||
dummy_dtor, dummy_dtor_data);
|
||||
|
||||
/* dummy_frame_context_saver_setup must be called last so that its
|
||||
saving of inferior registers gets called first (before possible
|
||||
DUMMY_DTOR destructor). */
|
||||
context_saver = dummy_frame_context_saver_setup (dummy_id, inferior_ptid);
|
||||
context_saver_cleanup = make_cleanup (dummy_frame_context_saver_cleanup,
|
||||
context_saver);
|
||||
|
||||
/* Register a clean-up for unwind_on_terminating_exception_breakpoint. */
|
||||
terminate_bp_cleanup = make_cleanup (cleanup_delete_std_terminate_breakpoint,
|
||||
NULL);
|
||||
|
@ -1027,6 +1193,12 @@ call_function_by_hand_dummy (struct value *function,
|
|||
in a block so that it's only in scope during the time it's valid. */
|
||||
{
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
struct thread_fsm *saved_sm;
|
||||
struct call_thread_fsm *sm;
|
||||
|
||||
/* Save the current FSM. We'll override it. */
|
||||
saved_sm = tp->thread_fsm;
|
||||
tp->thread_fsm = NULL;
|
||||
|
||||
/* Save this thread's ptid, we need it later but the thread
|
||||
may have exited. */
|
||||
|
@ -1034,10 +1206,57 @@ call_function_by_hand_dummy (struct value *function,
|
|||
|
||||
/* Run the inferior until it stops. */
|
||||
|
||||
e = run_inferior_call (tp, real_pc);
|
||||
}
|
||||
/* Create the FSM used to manage the infcall. It tells infrun to
|
||||
not report the stop to the user, and captures the return value
|
||||
before the dummy frame is popped. run_inferior_call registers
|
||||
it with the thread ASAP. */
|
||||
sm = new_call_thread_fsm (gdbarch, function,
|
||||
values_type,
|
||||
struct_return || hidden_first_param_p,
|
||||
struct_addr);
|
||||
|
||||
observer_notify_inferior_call_post (call_thread_ptid, funaddr);
|
||||
e = run_inferior_call (sm, tp, real_pc);
|
||||
|
||||
observer_notify_inferior_call_post (call_thread_ptid, funaddr);
|
||||
|
||||
tp = find_thread_ptid (call_thread_ptid);
|
||||
if (tp != NULL)
|
||||
{
|
||||
/* The FSM should still be the same. */
|
||||
gdb_assert (tp->thread_fsm == &sm->thread_fsm);
|
||||
|
||||
if (thread_fsm_finished_p (tp->thread_fsm))
|
||||
{
|
||||
struct value *retval;
|
||||
|
||||
/* The inferior call is successful. Pop the dummy frame,
|
||||
which runs its destructors and restores the inferior's
|
||||
suspend state, and restore the inferior control
|
||||
state. */
|
||||
dummy_frame_pop (dummy_id, call_thread_ptid);
|
||||
restore_infcall_control_state (inf_status);
|
||||
|
||||
/* Get the return value. */
|
||||
retval = sm->return_value;
|
||||
|
||||
/* Clean up / destroy the call FSM, and restore the
|
||||
original one. */
|
||||
thread_fsm_clean_up (tp->thread_fsm);
|
||||
thread_fsm_delete (tp->thread_fsm);
|
||||
tp->thread_fsm = saved_sm;
|
||||
|
||||
maybe_remove_breakpoints ();
|
||||
|
||||
do_cleanups (terminate_bp_cleanup);
|
||||
gdb_assert (retval != NULL);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Didn't complete. Restore previous state machine, and
|
||||
handle the error. */
|
||||
tp->thread_fsm = saved_sm;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rethrow an error if we got one trying to run the inferior. */
|
||||
|
||||
|
@ -1119,7 +1338,6 @@ When the function is done executing, GDB will silently stop."),
|
|||
name);
|
||||
}
|
||||
|
||||
if (stopped_by_random_signal || stop_stack_dummy != STOP_STACK_DUMMY)
|
||||
{
|
||||
/* Make a copy as NAME may be in an objfile freed by dummy_frame_pop. */
|
||||
char *name = xstrdup (get_function_name (funaddr,
|
||||
|
@ -1221,65 +1439,10 @@ When the function is done executing, GDB will silently stop."),
|
|||
name);
|
||||
}
|
||||
|
||||
/* The above code errors out, so ... */
|
||||
internal_error (__FILE__, __LINE__, _("... should not be here"));
|
||||
}
|
||||
|
||||
do_cleanups (terminate_bp_cleanup);
|
||||
|
||||
/* If we get here the called FUNCTION ran to completion,
|
||||
and the dummy frame has already been popped. */
|
||||
|
||||
{
|
||||
struct value *retval = NULL;
|
||||
|
||||
/* Inferior call is successful. Restore the inferior status.
|
||||
At this stage, leave the RETBUF alone. */
|
||||
restore_infcall_control_state (inf_status);
|
||||
|
||||
if (TYPE_CODE (values_type) == TYPE_CODE_VOID)
|
||||
retval = allocate_value (values_type);
|
||||
else if (struct_return || hidden_first_param_p)
|
||||
{
|
||||
if (stack_temporaries)
|
||||
{
|
||||
retval = value_from_contents_and_address (values_type, NULL,
|
||||
struct_addr);
|
||||
push_thread_stack_temporary (inferior_ptid, retval);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = allocate_value (values_type);
|
||||
read_value_memory (retval, 0, 1, struct_addr,
|
||||
value_contents_raw (retval),
|
||||
TYPE_LENGTH (values_type));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = allocate_value (values_type);
|
||||
gdbarch_return_value (gdbarch, function, values_type,
|
||||
dummy_frame_context_saver_get_regs (context_saver),
|
||||
value_contents_raw (retval), NULL);
|
||||
if (stack_temporaries && class_or_union_p (values_type))
|
||||
{
|
||||
/* Values of class type returned in registers are copied onto
|
||||
the stack and their lval_type set to lval_memory. This is
|
||||
required because further evaluation of the expression
|
||||
could potentially invoke methods on the return value
|
||||
requiring GDB to evaluate the "this" pointer. To evaluate
|
||||
the this pointer, GDB needs the memory address of the
|
||||
value. */
|
||||
value_force_lval (retval, struct_addr);
|
||||
push_thread_stack_temporary (inferior_ptid, retval);
|
||||
}
|
||||
}
|
||||
|
||||
do_cleanups (context_saver_cleanup);
|
||||
|
||||
gdb_assert (retval);
|
||||
return retval;
|
||||
}
|
||||
/* The above code errors out, so ... */
|
||||
gdb_assert_not_reached ("... should not be here");
|
||||
}
|
||||
|
||||
|
||||
|
|
157
gdb/infrun.c
157
gdb/infrun.c
|
@ -3834,6 +3834,7 @@ fetch_inferior_event (void *client_data)
|
|||
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 ();
|
||||
|
||||
|
@ -3853,12 +3854,21 @@ fetch_inferior_event (void *client_data)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
|
||||
cmd_done = 1;
|
||||
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)
|
||||
normal_stop ();
|
||||
|
||||
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
|
||||
cmd_done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7810,6 +7820,23 @@ print_stop_event (struct ui_out *uiout)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Here to return control to GDB when the inferior stops for real.
|
||||
Print appropriate messages, remove breakpoints, give terminal our modes.
|
||||
|
||||
|
@ -7903,16 +7930,7 @@ normal_stop (void)
|
|||
}
|
||||
|
||||
/* 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. */
|
||||
|
@ -7923,24 +7941,36 @@ normal_stop (void)
|
|||
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. */
|
||||
do_cleanups (old_chain);
|
||||
|
||||
/* 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);
|
||||
/* 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 ();
|
||||
|
||||
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_frame (get_current_frame ());
|
||||
|
||||
/* Set the current source location. */
|
||||
set_current_sal_from_frame (get_current_frame ());
|
||||
}
|
||||
|
||||
/* Look up the hook_stop and run it (CLI internally handles problem
|
||||
of stop_command's pre-hook not existing). */
|
||||
|
@ -7948,62 +7978,13 @@ normal_stop (void)
|
|||
catch_errors (hook_stop_stub, stop_command,
|
||||
"Error while running hook_stop:\n", RETURN_MASK_ALL);
|
||||
|
||||
if (!has_stack_frames ())
|
||||
goto done;
|
||||
|
||||
if (last.kind == TARGET_WAITKIND_SIGNALLED
|
||||
|| last.kind == TARGET_WAITKIND_EXITED)
|
||||
goto done;
|
||||
|
||||
/* 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:
|
||||
|
||||
/* Suppress the stop observer if we're in the middle of:
|
||||
|
||||
- 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. */
|
||||
|
||||
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);
|
||||
}
|
||||
/* 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 ();
|
||||
|
||||
|
|
|
@ -217,4 +217,8 @@ extern void mark_infrun_async_event_handler (void);
|
|||
to get past e.g., a breakpoint. */
|
||||
extern struct thread_info *step_over_queue_head;
|
||||
|
||||
/* Remove breakpoints if possible (usually that means, if everything
|
||||
is stopped). On failure, print a message. */
|
||||
extern void maybe_remove_breakpoints (void);
|
||||
|
||||
#endif /* INFRUN_H */
|
||||
|
|
|
@ -95,3 +95,13 @@ thread_fsm_async_reply_reason (struct thread_fsm *self)
|
|||
|
||||
return self->ops->async_reply_reason (self);
|
||||
}
|
||||
|
||||
/* See thread-fsm.h. */
|
||||
|
||||
int
|
||||
thread_fsm_should_notify_stop (struct thread_fsm *self)
|
||||
{
|
||||
if (self->ops->should_notify_stop != NULL)
|
||||
return self->ops->should_notify_stop (self);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,9 @@ struct thread_fsm_ops
|
|||
/* The async_reply_reason that is broadcast to MI clients if this
|
||||
FSM finishes successfully. */
|
||||
enum async_reply_reason (*async_reply_reason) (struct thread_fsm *self);
|
||||
|
||||
/* Whether the stop should be notified to the user/frontend. */
|
||||
int (*should_notify_stop) (struct thread_fsm *self);
|
||||
};
|
||||
/* Initialize FSM. */
|
||||
extern void thread_fsm_ctor (struct thread_fsm *fsm,
|
||||
|
@ -95,4 +98,7 @@ extern int thread_fsm_finished_p (struct thread_fsm *fsm);
|
|||
extern enum async_reply_reason
|
||||
thread_fsm_async_reply_reason (struct thread_fsm *fsm);
|
||||
|
||||
/* Calls the FSM's should_notify_stop method. */
|
||||
extern int thread_fsm_should_notify_stop (struct thread_fsm *self);
|
||||
|
||||
#endif /* THREAD_FSM_H */
|
||||
|
|
Loading…
Reference in a new issue