With dummy_frame destructors GDB no longer has to use global stop_registers.
dummy_frame's registers can be now stored associated with their specific
dummy_frame.
gdb/ChangeLog
2015-05-13 Jan Kratochvil <jan.kratochvil@redhat.com>
* infcall.c (struct dummy_frame_context_saver)
(dummy_frame_context_saver_data_free, dummy_frame_context_saver_dtor)
(dummy_frame_context_saver_drop, dummy_frame_context_saver_cleanup)
(dummy_frame_context_saver_get_regs, dummy_frame_context_saver_setup):
New.
(call_function_by_hand_dummy): Move discard_cleanups of
inf_status_cleanup before dummy_frame_push. Call
dummy_frame_context_saver_setup and prepare context_saver_cleanup.
Use dummy_frame_context_saver_get_regs instead of stop_registers.
* infcall.h (struct dummy_frame_context_saver)
(dummy_frame_context_saver_drop, dummy_frame_context_saver_cleanup)
(dummy_frame_context_saver_get_regs, dummy_frame_context_saver_setup):
New declarations.
* infcmd.c: Include infcall.h.
(get_return_value): Add parameter ctx_saver, use it instead of
stop_registers.
(print_return_value): Add parameter ctx_saver, pass it.
(struct finish_command_continuation_args): Add field ctx_saver.
(finish_command_continuation): Update print_return_value caller.
(finish_command_continuation_free_arg): Free also ctx_saver.
(finish_forward): Call dummy_frame_context_saver_setup.
* inferior.h (struct dummy_frame_context_saver): New declaration.
(get_return_value): Add parameter ctx_saver.
* python/py-finishbreakpoint.c (bpfinishpy_pre_stop_hook): Update
get_return_value caller.
+2015-05-13 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * infcall.c (struct dummy_frame_context_saver)
+ (dummy_frame_context_saver_data_free, dummy_frame_context_saver_dtor)
+ (dummy_frame_context_saver_drop, dummy_frame_context_saver_cleanup)
+ (dummy_frame_context_saver_get_regs, dummy_frame_context_saver_setup):
+ New.
+ (call_function_by_hand_dummy): Move discard_cleanups of
+ inf_status_cleanup before dummy_frame_push. Call
+ dummy_frame_context_saver_setup and prepare context_saver_cleanup.
+ Use dummy_frame_context_saver_get_regs instead of stop_registers.
+ * infcall.h (struct dummy_frame_context_saver)
+ (dummy_frame_context_saver_drop, dummy_frame_context_saver_cleanup)
+ (dummy_frame_context_saver_get_regs, dummy_frame_context_saver_setup):
+ New declarations.
+ * infcmd.c: Include infcall.h.
+ (get_return_value): Add parameter ctx_saver, use it instead of
+ stop_registers.
+ (print_return_value): Add parameter ctx_saver, pass it.
+ (struct finish_command_continuation_args): Add field ctx_saver.
+ (finish_command_continuation): Update print_return_value caller.
+ (finish_command_continuation_free_arg): Free also ctx_saver.
+ (finish_forward): Call dummy_frame_context_saver_setup.
+ * inferior.h (struct dummy_frame_context_saver): New declaration.
+ (get_return_value): Add parameter ctx_saver.
+ * python/py-finishbreakpoint.c (bpfinishpy_pre_stop_hook): Update
+ get_return_value caller.
+
2015-05-13 Jan Kratochvil <jan.kratochvil@redhat.com>
* dummy-frame.c (struct dummy_frame_dtor_list): New.
return call_function_by_hand_dummy (function, nargs, args, NULL, NULL);
}
+/* Data for dummy_frame_context_saver. Structure can be freed only
+ after both dummy_frame_context_saver_dtor and
+ dummy_frame_context_saver_drop have been called for it. */
+
+struct dummy_frame_context_saver
+{
+ /* Inferior registers fetched before associated dummy_frame got freed
+ and before any other destructors of associated dummy_frame got called.
+ It is initialized to NULL. */
+ struct regcache *retbuf;
+
+ /* It is 1 if this dummy_frame_context_saver_drop has been already
+ called. */
+ int drop_done;
+};
+
+/* Free struct dummy_frame_context_saver. */
+
+static void
+dummy_frame_context_saver_free (struct dummy_frame_context_saver *saver)
+{
+ regcache_xfree (saver->retbuf);
+ xfree (saver);
+}
+
+/* Destructor for associated dummy_frame. */
+
+static void
+dummy_frame_context_saver_dtor (void *data_voidp, int registers_valid)
+{
+ struct dummy_frame_context_saver *data = data_voidp;
+
+ gdb_assert (data->retbuf == NULL);
+
+ if (data->drop_done)
+ dummy_frame_context_saver_free (data);
+ else if (registers_valid)
+ data->retbuf = regcache_dup (get_current_regcache ());
+}
+
+/* Caller is no longer interested in this
+ struct dummy_frame_context_saver. After its associated dummy_frame
+ gets freed struct dummy_frame_context_saver can be also freed. */
+
+void
+dummy_frame_context_saver_drop (struct dummy_frame_context_saver *saver)
+{
+ saver->drop_done = 1;
+
+ if (!find_dummy_frame_dtor (dummy_frame_context_saver_dtor, saver))
+ dummy_frame_context_saver_free (saver);
+}
+
+/* Stub dummy_frame_context_saver_drop compatible with make_cleanup. */
+
+void
+dummy_frame_context_saver_cleanup (void *data)
+{
+ struct dummy_frame_context_saver *saver = data;
+
+ dummy_frame_context_saver_drop (saver);
+}
+
+/* Fetch RETBUF field of possibly opaque DTOR_DATA.
+ RETBUF must not be NULL. */
+
+struct regcache *
+dummy_frame_context_saver_get_regs (struct dummy_frame_context_saver *saver)
+{
+ gdb_assert (saver->retbuf != NULL);
+ return saver->retbuf;
+}
+
+/* Register provider of inferior registers at the time DUMMY_ID frame of
+ PTID gets freed (before inferior registers get restored to those
+ before dummy_frame). */
+
+struct dummy_frame_context_saver *
+dummy_frame_context_saver_setup (struct frame_id dummy_id, ptid_t ptid)
+{
+ struct dummy_frame_context_saver *saver;
+
+ saver = xmalloc (sizeof (*saver));
+ saver->retbuf = NULL;
+ saver->drop_done = 0;
+ register_dummy_frame_dtor (dummy_id, inferior_ptid,
+ dummy_frame_context_saver_dtor, saver);
+ return saver;
+}
+
/* All this stuff with a dummy frame may seem unnecessarily complicated
(why not just save registers in GDB?). The purpose of pushing a dummy
frame which looks just like a real frame is so that if you call a
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));
if (unwind_on_terminating_exception_p)
set_std_terminate_breakpoint ();
+ /* Discard both inf_status and caller_state cleanups.
+ From this point on we explicitly restore the associated state
+ or discard it. */
+ discard_cleanups (inf_status_cleanup);
+
/* Everything's ready, push all the info needed to restore the
caller (and identify the dummy-frame) onto the dummy-frame
stack. */
register_dummy_frame_dtor (dummy_id, inferior_ptid,
dummy_dtor, dummy_dtor_data);
- /* Discard both inf_status and caller_state cleanups.
- From this point on we explicitly restore the associated state
- or discard it. */
- discard_cleanups (inf_status_cleanup);
+ /* 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,
and the dummy frame has already been popped. */
{
- struct address_space *aspace = get_regcache_aspace (stop_registers);
- struct regcache *retbuf = regcache_xmalloc (gdbarch, aspace);
- struct cleanup *retbuf_cleanup = make_cleanup_regcache_xfree (retbuf);
struct value *retval = NULL;
- regcache_cpy_no_passthrough (retbuf, stop_registers);
-
/* Inferior call is successful. Restore the inferior status.
At this stage, leave the RETBUF alone. */
restore_infcall_control_state (inf_status);
{
retval = allocate_value (values_type);
gdbarch_return_value (gdbarch, function, values_type,
- retbuf, value_contents_raw (retval), NULL);
+ 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
}
}
- do_cleanups (retbuf_cleanup);
+ do_cleanups (context_saver_cleanup);
gdb_assert (retval);
return retval;
dummy_frame_dtor_ftype *dummy_dtor,
void *dummy_dtor_data);
+struct dummy_frame_context_saver;
+extern void dummy_frame_context_saver_drop
+ (struct dummy_frame_context_saver *data);
+extern void dummy_frame_context_saver_cleanup (void *data_voidp);
+extern struct regcache *dummy_frame_context_saver_get_regs
+ (struct dummy_frame_context_saver *saver);
+extern struct dummy_frame_context_saver *dummy_frame_context_saver_setup
+ (struct frame_id dummy_id, ptid_t ptid);
+
#endif
#include "continuations.h"
#include "linespec.h"
#include "cli/cli-utils.h"
+#include "infcall.h"
/* Local functions: */
}
\f
/* Return the value of the result of a function at the end of a 'finish'
- command/BP. */
+ command/BP. DTOR_DATA (if not NULL) can represent inferior registers
+ right after an inferior call has finished. */
struct value *
-get_return_value (struct value *function, struct type *value_type)
+get_return_value (struct value *function, struct type *value_type,
+ struct dummy_frame_context_saver *ctx_saver)
{
- struct regcache *stop_regs = stop_registers;
+ struct regcache *stop_regs = NULL;
struct gdbarch *gdbarch;
struct value *value;
struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
/* If stop_registers were not saved, use the current registers. */
- if (!stop_regs)
+ if (ctx_saver != NULL)
+ stop_regs = dummy_frame_context_saver_get_regs (ctx_saver);
+ else
{
stop_regs = regcache_dup (get_current_regcache ());
make_cleanup_regcache_xfree (stop_regs);
return value;
}
-/* Print the result of a function at the end of a 'finish' command. */
+/* Print the result of a function at the end of a 'finish' command.
+ DTOR_DATA (if not NULL) can represent inferior registers right after
+ an inferior call has finished. */
static void
-print_return_value (struct value *function, struct type *value_type)
+print_return_value (struct value *function, struct type *value_type,
+ struct dummy_frame_context_saver *ctx_saver)
{
- struct value *value = get_return_value (function, value_type);
+ struct value *value = get_return_value (function, value_type, ctx_saver);
struct ui_out *uiout = current_uiout;
if (value)
int thread;
struct breakpoint *breakpoint;
struct symbol *function;
+
+ /* Inferior registers stored right before dummy_frame has been freed
+ after an inferior call. It can be NULL if no inferior call was
+ involved, GDB will then use current inferior registers. */
+ struct dummy_frame_context_saver *ctx_saver;
};
static void
/* print_return_value can throw an exception in some
circumstances. We need to catch this so that we still
delete the breakpoint. */
- print_return_value (func, value_type);
+ print_return_value (func, value_type, a->ctx_saver);
}
CATCH (ex, RETURN_MASK_ALL)
{
static void
finish_command_continuation_free_arg (void *arg)
{
- xfree (arg);
+ struct finish_command_continuation_args *cargs = arg;
+
+ if (cargs->ctx_saver != NULL)
+ dummy_frame_context_saver_drop (cargs->ctx_saver);
+ xfree (cargs);
}
/* finish_backward -- helper function for finish_command. */
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
struct breakpoint *breakpoint;
- struct cleanup *old_chain;
+ struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
struct finish_command_continuation_args *cargs;
int thread = tp->num;
+ struct dummy_frame_context_saver *saver = NULL;
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
+ if (get_frame_type (frame) == DUMMY_FRAME)
+ {
+ saver = dummy_frame_context_saver_setup (get_stack_frame_id (frame),
+ inferior_ptid);
+ make_cleanup (dummy_frame_context_saver_cleanup, saver);
+ }
+
breakpoint = set_momentary_breakpoint (gdbarch, sal,
get_stack_frame_id (frame),
bp_finish);
/* set_momentary_breakpoint invalidates FRAME. */
frame = NULL;
- old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ make_cleanup_delete_breakpoint (breakpoint);
set_longjmp_breakpoint (tp, frame_id);
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
cargs->thread = thread;
cargs->breakpoint = breakpoint;
cargs->function = function;
+ cargs->ctx_saver = saver;
add_continuation (tp, finish_command_continuation, cargs,
finish_command_continuation_free_arg);
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
extern void notice_new_inferior (ptid_t, int, int);
-extern struct value *get_return_value (struct value *function,
- struct type *value_type);
+struct dummy_frame_context_saver;
+extern struct value *get_return_value
+ (struct value *function, struct type *value_type,
+ struct dummy_frame_context_saver *ctx_saver);
/* Prepare for execution command. TARGET is the target that will run
the command. BACKGROUND determines whether this is a foreground
value_object_to_value (self_finishbp->function_value);
struct type *value_type =
type_object_to_type (self_finishbp->return_type);
- struct value *ret = get_return_value (function, value_type);
+
+ /* bpfinishpy_init cannot finish into DUMMY_FRAME (throws an error
+ in such case) so it is OK to always pass CTX_SAVER as NULL. */
+ struct value *ret = get_return_value (function, value_type, NULL);
if (ret)
{