aboutsummaryrefslogtreecommitdiff
path: root/gdb/infcall.c
diff options
context:
space:
mode:
authorDoug Evans <dje@google.com>2009-01-19 19:05:01 +0000
committerDoug Evans <dje@google.com>2009-01-19 19:05:01 +0000
commitb89667ebd42a3a59b8f17ec9153d07dd2fc7e5e1 (patch)
tree8789d7e1d5edcf4019b51d276a77fc5fe649d699 /gdb/infcall.c
parent63f2573fe476336550cf141d09a62bc1a349eba2 (diff)
downloadgdb-b89667ebd42a3a59b8f17ec9153d07dd2fc7e5e1.zip
gdb-b89667ebd42a3a59b8f17ec9153d07dd2fc7e5e1.tar.gz
gdb-b89667ebd42a3a59b8f17ec9153d07dd2fc7e5e1.tar.bz2
* dummy-frame.c (dummy_frame): Replace regcache member with
caller_state. (dummy_frame_push): Replace caller_regcache arg with caller_state. All callers updated. (remove_dummy_frame,pop_dummy_frame,lookup_dummy_frame): New fns. (dummy_frame_pop): Rewrite. Verify requested frame is in the dummy frame stack. Restore program state. (cleanup_dummy_frames): Rewrite. (dummy_frame_sniffer): Update. Make static. * dummy-frame.h (regcache,frame_info): Delete forward decls. (inferior_thread_state): New forward decl. (dummy_frame_push): Update prototype. * frame.c (frame_pop): dummy_frame_pop now does all the work for DUMMY_FRAMEs. * infcall.c (breakpoint_auto_delete_contents): Delete. (get_function_name,run_inferior_call): New fns. (call_function_by_hand): Simplify by moving some code to get_function_name, run_inferior_call. Inferior function call wrapped in TRY_CATCH so there's less need for cleanups and all exits from proceed are handled similarily. Detect program exit. Detect program stopping in a different thread. Make error messages more consistent. * inferior.h (inferior_thread_state): Declare (opaque type). (save_inferior_thread_state,restore_inferior_thread_state, make_cleanup_restore_inferior_thread_state, discard_inferior_thread_state, get_inferior_thread_state_regcache): Declare. (save_inferior_status): Update prototype. * infrun.c: (normal_stop): When stopped for the completion of an inferior function call, verify the expected stack frame kind. (inferior_thread_state): New struct. (save_inferior_thread_state,restore_inferior_thread_state, do_restore_inferior_thread_state_cleanup, make_cleanup_restore_inferior_thread_state, discard_inferior_thread_state, get_inferior_thread_state_regcache): New functions. (inferior_status): Move stop_signal, stop_pc, registers to inferior_thread_state. Remove restore_stack_info. (save_inferior_status): Remove arg restore_stack_info. All callers updated. Remove saving of state now saved by save_inferior_thread_state. (restore_inferior_status): Remove restoration of state now done by restore_inferior_thread_state. (discard_inferior_status): Remove freeing of registers, now done by discard_inferior_thread_state. * gdb.base/break.exp: Update expected gdb output. * gdb.base/sepdebug.exp: Ditto. * gdb.mi/mi-syn-frame.exp: Ditto. * gdb.mi/mi2-syn-frame.exp: Ditto. * gdb.base/call-signal-resume.exp: New file. * gdb.base/call-signals.c: New file. * gdb.base/unwindonsignal.exp: New file. * gdb.base/unwindonsignal.c: New file. * gdb.threads/interrupted-hand-call.exp: New file. * gdb.threads/interrupted-hand-call.c: New file. * gdb.threads/thread-unwindonsignal.exp: New file.
Diffstat (limited to 'gdb/infcall.c')
-rw-r--r--gdb/infcall.c375
1 files changed, 244 insertions, 131 deletions
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 6b42774..d6da8b2 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -36,6 +36,13 @@
#include "dummy-frame.h"
#include "ada-lang.h"
#include "gdbthread.h"
+#include "exceptions.h"
+
+/* If we can't find a function's name from its address,
+ we print this instead. */
+#define RAW_FUNCTION_ADDRESS_FORMAT "at 0x%s"
+#define RAW_FUNCTION_ADDRESS_SIZE (sizeof (RAW_FUNCTION_ADDRESS_FORMAT) \
+ + 2 * sizeof (CORE_ADDR))
/* NOTE: cagney/2003-04-16: What's the future of this code?
@@ -260,16 +267,6 @@ find_function_addr (struct value *function, struct type **retval_type)
return funaddr + gdbarch_deprecated_function_start_offset (current_gdbarch);
}
-/* Call breakpoint_auto_delete on the current contents of the bpstat
- of the current thread. */
-
-static void
-breakpoint_auto_delete_contents (void *arg)
-{
- if (!ptid_equal (inferior_ptid, null_ptid))
- breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
-}
-
/* For CALL_DUMMY_ON_STACK, push a breakpoint sequence that the called
function returns to. */
@@ -288,6 +285,103 @@ push_dummy_code (struct gdbarch *gdbarch,
regcache);
}
+/* Fetch the name of the function at FUNADDR.
+ This is used in printing an error message for call_function_by_hand.
+ BUF is used to print FUNADDR in hex if the function name cannot be
+ determined. It must be large enough to hold formatted result of
+ RAW_FUNCTION_ADDRESS_FORMAT. */
+
+static const char *
+get_function_name (CORE_ADDR funaddr, char *buf, int buf_size)
+{
+ {
+ struct symbol *symbol = find_pc_function (funaddr);
+ if (symbol)
+ return SYMBOL_PRINT_NAME (symbol);
+ }
+
+ {
+ /* Try the minimal symbols. */
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
+ if (msymbol)
+ return SYMBOL_PRINT_NAME (msymbol);
+ }
+
+ {
+ char *tmp = xstrprintf (_(RAW_FUNCTION_ADDRESS_FORMAT),
+ hex_string (funaddr));
+ gdb_assert (strlen (tmp) + 1 <= buf_size);
+ strcpy (buf, tmp);
+ xfree (tmp);
+ return buf;
+ }
+}
+
+/* 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
+ reason >= 0 if there's no error.
+
+ This is done inside a TRY_CATCH so the caller needn't worry about
+ 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)
+{
+ volatile struct gdb_exception e;
+ int saved_async = 0;
+ int saved_suppress_resume_observer = suppress_resume_observer;
+ int saved_suppress_stop_observer = suppress_stop_observer;
+ ptid_t call_thread_ptid = call_thread->ptid;
+ char *saved_target_shortname = xstrdup (target_shortname);
+
+ clear_proceed_status ();
+
+ disable_watchpoints_before_interactive_call_start ();
+ call_thread->proceed_to_finish = 1; /* We want stop_registers, please... */
+
+ if (target_can_async_p ())
+ saved_async = target_async_mask (0);
+
+ suppress_resume_observer = 1;
+ suppress_stop_observer = 1;
+
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ proceed (real_pc, TARGET_SIGNAL_0, 0);
+
+ /* At this point the current thread may have changed.
+ CALL_THREAD is no longer usable as its thread may have exited.
+ Set it to NULL to prevent its further use. */
+ call_thread = NULL;
+
+ suppress_resume_observer = saved_suppress_resume_observer;
+ suppress_stop_observer = saved_suppress_stop_observer;
+
+ /* Don't restore the async mask if the target has changed,
+ saved_async is for the original target. */
+ if (saved_async
+ && strcmp (saved_target_shortname, target_shortname) == 0)
+ target_async_mask (saved_async);
+
+ enable_watchpoints_after_interactive_call_stop ();
+
+ /* Call breakpoint_auto_delete on the current contents of the bpstat
+ of inferior call thread.
+ If all error()s out of proceed ended up calling normal_stop
+ (and perhaps they should; it already does in the special case
+ of error out of resume()), then we wouldn't need this. */
+ if (e.reason < 0)
+ {
+ struct thread_info *tp = find_thread_pid (call_thread_ptid);
+ if (tp != NULL)
+ breakpoint_auto_delete (tp->stop_bpstat);
+ }
+
+ xfree (saved_target_shortname);
+
+ return e;
+}
+
/* 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
@@ -313,20 +407,22 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
struct type *values_type, *target_values_type;
unsigned char struct_return = 0, lang_struct_return = 0;
CORE_ADDR struct_addr = 0;
- struct regcache *retbuf;
- struct cleanup *retbuf_cleanup;
struct inferior_status *inf_status;
struct cleanup *inf_status_cleanup;
+ struct inferior_thread_state *caller_state;
+ struct cleanup *caller_state_cleanup;
CORE_ADDR funaddr;
CORE_ADDR real_pc;
struct type *ftype = check_typedef (value_type (function));
CORE_ADDR bp_addr;
- struct regcache *caller_regcache;
- struct cleanup *caller_regcache_cleanup;
struct frame_id dummy_id;
struct cleanup *args_cleanup;
struct frame_info *frame;
struct gdbarch *gdbarch;
+ ptid_t call_thread_ptid;
+ struct gdb_exception e;
+ const char *name;
+ char name_buf[RAW_FUNCTION_ADDRESS_SIZE];
if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
@@ -340,25 +436,18 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
if (!gdbarch_push_dummy_call_p (gdbarch))
error (_("This target does not support function calls."));
- /* Create a cleanup chain that contains the retbuf (buffer
- containing the register values). This chain is create BEFORE the
- inf_status chain so that the inferior status can cleaned up
- (restored or discarded) without having the retbuf freed. */
- retbuf = regcache_xmalloc (gdbarch);
- retbuf_cleanup = make_cleanup_regcache_xfree (retbuf);
-
- /* A cleanup for the inferior status. Create this AFTER the retbuf
- so that this can be discarded or applied without interfering with
- the regbuf. */
- inf_status = save_inferior_status (1);
+ /* A cleanup for the inferior status.
+ This is only needed while we're preparing the inferior function call. */
+ inf_status = save_inferior_status ();
inf_status_cleanup = make_cleanup_restore_inferior_status (inf_status);
- /* Save the caller's registers so that they can be restored once the
+ /* Save the caller's registers and other state associated with the
+ inferior itself so that they can be restored once the
callee returns. To allow nested calls the registers are (further
down) pushed onto a dummy frame stack. Include a cleanup (which
is tossed once the regcache has been pushed). */
- caller_regcache = frame_save_as_regcache (frame);
- caller_regcache_cleanup = make_cleanup_regcache_xfree (caller_regcache);
+ caller_state = save_inferior_thread_state ();
+ caller_state_cleanup = make_cleanup_restore_inferior_thread_state (caller_state);
/* Ensure that the initial SP is correctly aligned. */
{
@@ -633,99 +722,117 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
/* Everything's ready, push all the info needed to restore the
caller (and identify the dummy-frame) onto the dummy-frame
stack. */
- dummy_frame_push (caller_regcache, &dummy_id);
- discard_cleanups (caller_regcache_cleanup);
+ dummy_frame_push (caller_state, &dummy_id);
+
+ /* 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);
/* - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP -
If you're looking to implement asynchronous dummy-frames, then
just below is the place to chop this function in two.. */
- /* Now proceed, having reached the desired place. */
- clear_proceed_status ();
-
- /* Execute a "stack dummy", a piece of code stored in the stack by
- the debugger to be executed in the inferior.
-
- The dummy's frame is automatically popped whenever that break is
- hit. If that is the first time the program stops,
- call_function_by_hand returns to its caller with that frame
- already gone and sets RC to 0.
-
- Otherwise, set RC to a non-zero value. If the called function
- receives a random signal, we do not allow the user to continue
- executing it as this may not work. The dummy frame is poped and
- we return 1. If we hit a breakpoint, we leave the frame in place
- and return 2 (the frame will eventually be popped when we do hit
- the dummy end breakpoint). */
-
+ /* TP is invalid after run_inferior_call returns, so enclose this
+ in a block so that it's only in scope during the time it's valid. */
{
- struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0);
- struct cleanup *old_cleanups2;
- int saved_async = 0;
struct thread_info *tp = inferior_thread ();
- /* If all error()s out of proceed ended up calling normal_stop
- (and perhaps they should; it already does in the special case
- of error out of resume()), then we wouldn't need this. */
- make_cleanup (breakpoint_auto_delete_contents, NULL);
-
- disable_watchpoints_before_interactive_call_start ();
- tp->proceed_to_finish = 1; /* We want stop_registers, please... */
+ /* Save this thread's ptid, we need it later but the thread
+ may have exited. */
+ call_thread_ptid = tp->ptid;
- if (target_can_async_p ())
- saved_async = target_async_mask (0);
+ /* Run the inferior until it stops. */
- old_cleanups2 = make_cleanup_restore_integer (&suppress_resume_observer);
- suppress_resume_observer = 1;
- make_cleanup_restore_integer (&suppress_stop_observer);
- suppress_stop_observer = 1;
- proceed (real_pc, TARGET_SIGNAL_0, 0);
- do_cleanups (old_cleanups2);
-
- if (saved_async)
- target_async_mask (saved_async);
-
- enable_watchpoints_after_interactive_call_stop ();
-
- discard_cleanups (old_cleanups);
+ e = run_inferior_call (tp, real_pc);
}
+ /* Rethrow an error if we got one trying to run the inferior. */
+
+ if (e.reason < 0)
+ {
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ discard_inferior_status (inf_status);
+
+ /* We could discard the dummy frame here if the program exited,
+ but it will get garbage collected the next time the program is
+ run anyway. */
+
+ switch (e.reason)
+ {
+ case RETURN_ERROR:
+ throw_error (e.error, _("\
+%s\n\
+An error occurred while in a function called from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ e.message, name);
+ case RETURN_QUIT:
+ default:
+ throw_exception (e);
+ }
+ }
+
+ /* If the program has exited, or we stopped at a different thread,
+ exit and inform the user. */
+
if (! target_has_execution)
{
- /* If we try to restore the inferior status (via the cleanup),
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ /* If we try to restore the inferior status,
we'll crash as the inferior is no longer running. */
- discard_cleanups (inf_status_cleanup);
discard_inferior_status (inf_status);
+
+ /* We could discard the dummy frame here given that the program exited,
+ but it will get garbage collected the next time the program is
+ run anyway. */
+
error (_("\
-The program being debugged exited while in a function called from GDB."));
+The program being debugged exited while in a function called from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned."),
+ name);
+ }
+
+ if (! ptid_equal (call_thread_ptid, inferior_ptid))
+ {
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
+ /* We've switched threads. This can happen if another thread gets a
+ signal or breakpoint while our thread was running.
+ There's no point in restoring the inferior status,
+ we're in a different thread. */
+ discard_inferior_status (inf_status);
+ /* Keep the dummy frame record, if the user switches back to the
+ thread with the hand-call, we'll need it. */
+ if (stopped_by_random_signal)
+ error (_("\
+The program received a signal in another thread while\n\
+making a function call from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ name);
+ else
+ error (_("\
+The program stopped in another thread while making a function call from GDB.\n\
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
+ name);
}
if (stopped_by_random_signal || !stop_stack_dummy)
{
- /* Find the name of the function we're about to complain about. */
- const char *name = NULL;
- {
- struct symbol *symbol = find_pc_function (funaddr);
- if (symbol)
- name = SYMBOL_PRINT_NAME (symbol);
- else
- {
- /* Try the minimal symbols. */
- struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
- if (msymbol)
- name = SYMBOL_PRINT_NAME (msymbol);
- }
- if (name == NULL)
- {
- /* Can't use a cleanup here. It is discarded, instead use
- an alloca. */
- char *tmp = xstrprintf ("at %s", hex_string (funaddr));
- char *a = alloca (strlen (tmp) + 1);
- strcpy (a, tmp);
- xfree (tmp);
- name = a;
- }
- }
+ const char *name = get_function_name (funaddr,
+ name_buf, sizeof (name_buf));
+
if (stopped_by_random_signal)
{
/* We stopped inside the FUNCTION because of a random
@@ -737,8 +844,12 @@ The program being debugged exited while in a function called from GDB."));
/* The user wants the context restored. */
/* We must get back to the frame we were before the
- dummy call. */
- frame_pop (get_current_frame ());
+ dummy call. */
+ dummy_frame_pop (dummy_id);
+
+ /* We also need to restore inferior status to that before the
+ dummy call. */
+ restore_inferior_status (inf_status);
/* FIXME: Insert a bunch of wrap_here; name can be very
long if it's a C++ name with arguments and stuff. */
@@ -746,41 +857,39 @@ The program being debugged exited while in a function called from GDB."));
The program being debugged was signaled while in a function called from GDB.\n\
GDB has restored the context to what it was before the call.\n\
To change this behavior use \"set unwindonsignal off\".\n\
-Evaluation of the expression containing the function (%s) will be abandoned."),
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned."),
name);
}
else
{
/* The user wants to stay in the frame where we stopped
- (default).*/
- /* If we restored the inferior status (via the cleanup),
- we would print a spurious error message (Unable to
- restore previously selected frame), would write the
- registers from the inf_status (which is wrong), and
- would do other wrong things. */
- discard_cleanups (inf_status_cleanup);
+ (default).
+ Discard inferior status, we're not at the same point
+ we started at. */
discard_inferior_status (inf_status);
+
/* FIXME: Insert a bunch of wrap_here; name can be very
long if it's a C++ name with arguments and stuff. */
error (_("\
The program being debugged was signaled while in a function called from GDB.\n\
GDB remains in the frame where the signal was received.\n\
To change this behavior use \"set unwindonsignal on\".\n\
-Evaluation of the expression containing the function (%s) will be abandoned."),
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+When the function is done executing, GDB will silently stop."),
name);
}
}
if (!stop_stack_dummy)
{
- /* We hit a breakpoint inside the FUNCTION. */
- /* If we restored the inferior status (via the cleanup), we
- would print a spurious error message (Unable to restore
- previously selected frame), would write the registers
- from the inf_status (which is wrong), and would do other
- wrong things. */
- discard_cleanups (inf_status_cleanup);
+ /* We hit a breakpoint inside the FUNCTION.
+ Keep the dummy frame, the user may want to examine its state.
+ Discard inferior status, we're not at the same point
+ we started at. */
discard_inferior_status (inf_status);
+
/* The following error message used to say "The expression
which contained the function call has been discarded."
It is a hard concept to explain in a few words. Ideally,
@@ -791,28 +900,32 @@ Evaluation of the expression containing the function (%s) will be abandoned."),
a C++ name with arguments and stuff. */
error (_("\
The program being debugged stopped while in a function called from GDB.\n\
-When the function (%s) is done executing, GDB will silently\n\
-stop (instead of continuing to evaluate the expression containing\n\
-the function call)."), name);
+Evaluation of the expression containing the function\n\
+(%s) will be abandoned.\n\
+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"));
}
- /* If we get here the called FUNCTION run to completion. */
+ /* If we get here the called FUNCTION ran to completion,
+ and the dummy frame has already been popped. */
- /* On normal return, the stack dummy has been popped already. */
- regcache_cpy_no_passthrough (retbuf, stop_registers);
-
- /* Restore the inferior status, via its cleanup. At this stage,
- leave the RETBUF alone. */
- do_cleanups (inf_status_cleanup);
-
- /* Figure out the value returned by the function. */
{
+ struct regcache *retbuf = regcache_xmalloc (gdbarch);
+ 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_inferior_status (inf_status);
+
+ /* Figure out the value returned by the function. */
+
if (lang_struct_return)
retval = value_at (values_type, struct_addr);
else if (TYPE_CODE (target_values_type) == TYPE_CODE_VOID)
@@ -841,7 +954,7 @@ the function call)."), name);
do_cleanups (retbuf_cleanup);
- gdb_assert(retval);
+ gdb_assert (retval);
return retval;
}
}