Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008 Free Software Foundation, Inc.
+ 2008, 2009 Free Software Foundation, Inc.
This file is part of GDB.
#include "language.h"
#include "solib.h"
#include "main.h"
-
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
static int currently_stepping (struct thread_info *tp);
+static int currently_stepping_callback (struct thread_info *tp, void *data);
+
static void xdb_handle_command (char *args, int from_tty);
static int prepare_to_proceed (int);
int stop_after_trap;
-/* Save register contents here when about to pop a stack dummy frame,
- if-and-only-if proceed_to_finish is set.
+/* Save register contents here when executing a "finish" command or are
+ about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
Thus this contains the return value from the called function (assuming
values are returned in a register). */
{
int should_resume = 1;
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
+
+ /* Note that these must be reset if we follow a fork below. */
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct thread_info *tp = inferior_thread ();
CORE_ADDR pc = regcache_read_pc (regcache);
+
QUIT;
if (debug_infrun)
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
if (follow_fork ())
should_resume = 0;
+
+ /* Following a child fork will change our notion of current
+ thread. */
+ tp = inferior_thread ();
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+ pc = regcache_read_pc (regcache);
break;
case TARGET_WAITKIND_EXECD:
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
}
- target_resume (resume_ptid, step, sig);
-
/* Avoid confusing the next resume, if the next stop/resume
happens to apply to another thread. */
tp->stop_signal = TARGET_SIGNAL_0;
+
+ target_resume (resume_ptid, step, sig);
}
discard_cleanups (old_cleanups);
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
-void
-clear_proceed_status (void)
+static void
+clear_proceed_status_thread (struct thread_info *tp)
{
- if (!ptid_equal (inferior_ptid, null_ptid))
- {
- struct thread_info *tp;
- struct inferior *inferior;
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: clear_proceed_status_thread (%s)\n",
+ target_pid_to_str (tp->ptid));
- tp = inferior_thread ();
+ tp->trap_expected = 0;
+ tp->step_range_start = 0;
+ tp->step_range_end = 0;
+ tp->step_frame_id = null_frame_id;
+ tp->step_over_calls = STEP_OVER_UNDEBUGGABLE;
+ tp->stop_requested = 0;
+
+ tp->stop_step = 0;
- tp->trap_expected = 0;
- tp->step_range_start = 0;
- tp->step_range_end = 0;
- tp->step_frame_id = null_frame_id;
- tp->step_over_calls = STEP_OVER_UNDEBUGGABLE;
- tp->stop_requested = 0;
+ tp->proceed_to_finish = 0;
- tp->stop_step = 0;
+ /* Discard any remaining commands or status from previous stop. */
+ bpstat_clear (&tp->stop_bpstat);
+}
- tp->proceed_to_finish = 0;
+static int
+clear_proceed_status_callback (struct thread_info *tp, void *data)
+{
+ if (is_exited (tp->ptid))
+ return 0;
- /* Discard any remaining commands or status from previous
- stop. */
- bpstat_clear (&tp->stop_bpstat);
+ clear_proceed_status_thread (tp);
+ return 0;
+}
+void
+clear_proceed_status (void)
+{
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ {
+ struct inferior *inferior;
+
+ if (non_stop)
+ {
+ /* If in non-stop mode, only delete the per-thread status
+ of the current thread. */
+ clear_proceed_status_thread (inferior_thread ());
+ }
+ else
+ {
+ /* In all-stop mode, delete the per-thread status of
+ *all* threads. */
+ iterate_over_threads (clear_proceed_status_callback, NULL);
+ }
+
inferior = current_inferior ();
inferior->stop_soon = NO_STOP_QUIETLY;
}
stop_after_trap = 0;
- breakpoint_proceeded = 1; /* We're about to proceed... */
+
+ observer_notify_about_to_proceed ();
if (stop_registers)
{
struct thread_info *tp;
CORE_ADDR pc = regcache_read_pc (regcache);
int oneproc = 0;
- enum target_signal stop_signal;
if (step > 0)
step_start_function = find_pc_function (pc);
Cleanup local state that assumed the PTID was to be resumed, and
report the stop to the frontend. */
-void
+static void
infrun_thread_stop_requested (ptid_t ptid)
{
struct displaced_step_request *it, *next, *prev = NULL;
iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
}
+void nullify_last_target_wait_ptid (void);
+
+static void
+infrun_thread_thread_exit (struct thread_info *tp, int silent)
+{
+ if (ptid_equal (target_last_wait_ptid, tp->ptid))
+ nullify_last_target_wait_ptid ();
+}
+
/* Callback for iterate_over_threads. */
static int
delete_step_thread_step_resume_breakpoint ();
}
+/* Pretty print the results of target_wait, for debugging purposes. */
+
+static void
+print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
+ const struct target_waitstatus *ws)
+{
+ char *status_string = target_waitstatus_to_string (ws);
+ struct ui_file *tmp_stream = mem_fileopen ();
+ char *text;
+ long len;
+
+ /* The text is split over several lines because it was getting too long.
+ Call fprintf_unfiltered (gdb_stdlog) once so that the text is still
+ output as a unit; we want only one timestamp printed if debug_timestamp
+ is set. */
+
+ fprintf_unfiltered (tmp_stream,
+ "infrun: target_wait (%d", PIDGET (waiton_ptid));
+ if (PIDGET (waiton_ptid) != -1)
+ fprintf_unfiltered (tmp_stream,
+ " [%s]", target_pid_to_str (waiton_ptid));
+ fprintf_unfiltered (tmp_stream, ", status) =\n");
+ fprintf_unfiltered (tmp_stream,
+ "infrun: %d [%s],\n",
+ PIDGET (result_ptid), target_pid_to_str (result_ptid));
+ fprintf_unfiltered (tmp_stream,
+ "infrun: %s\n",
+ status_string);
+
+ text = ui_file_xstrdup (tmp_stream, &len);
+
+ /* This uses %s in part to handle %'s in the text, but also to avoid
+ a gcc error: the format attribute requires a string literal. */
+ fprintf_unfiltered (gdb_stdlog, "%s", text);
+
+ xfree (status_string);
+ xfree (text);
+ ui_file_delete (tmp_stream);
+}
+
/* Wait for control to return from inferior to debugger.
If TREAT_EXEC_AS_SIGTRAP is non-zero, then handle EXEC signals
while (1)
{
+ struct cleanup *old_chain;
+
if (deprecated_target_wait_hook)
ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws);
else
ecs->ptid = target_wait (waiton_ptid, &ecs->ws);
+ if (debug_infrun)
+ print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+
if (treat_exec_as_sigtrap && ecs->ws.kind == TARGET_WAITKIND_EXECD)
{
xfree (ecs->ws.value.execd_pathname);
ecs->ws.value.sig = TARGET_SIGNAL_TRAP;
}
+ /* If an error happens while handling the event, propagate GDB's
+ knowledge of the executing state to the frontend/user running
+ state. */
+ old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
+ /* No error, don't finish the state yet. */
+ discard_cleanups (old_chain);
+
if (!ecs->wait_some_more)
break;
}
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+ struct cleanup *ts_old_chain;
int was_sync = sync_execution;
memset (ecs, 0, sizeof (*ecs));
else
ecs->ptid = target_wait (waiton_ptid, &ecs->ws);
+ if (debug_infrun)
+ print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+
if (non_stop
&& ecs->ws.kind != TARGET_WAITKIND_IGNORE
&& ecs->ws.kind != TARGET_WAITKIND_EXITED
thread. */
context_switch (ecs->ptid);
+ /* If an error happens while handling the event, propagate GDB's
+ knowledge of the executing state to the frontend/user running
+ state. */
+ if (!non_stop)
+ ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+ else
+ ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid);
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
}
+ /* No error, don't finish the thread states yet. */
+ discard_cleanups (ts_old_chain);
+
/* Revert thread and frame. */
do_cleanups (old_chain);
{
breakpoint_retire_moribund ();
- /* Mark the non-executing threads accordingly. */
- if (!non_stop
- || ecs->ws.kind == TARGET_WAITKIND_EXITED
- || ecs->ws.kind == TARGET_WAITKIND_SIGNALLED)
- set_executing (pid_to_ptid (-1), 0);
- else
- set_executing (ecs->ptid, 0);
+ /* Mark the non-executing threads accordingly. In all-stop, all
+ threads of all processes are stopped when we get any event
+ reported. In non-stop mode, only the event thread stops. If
+ we're handling a process exit in non-stop mode, there's
+ nothing to do, as threads of the dead process are gone, and
+ threads of any other process were left running. */
+ if (!non_stop)
+ set_executing (minus_one_ptid, 0);
+ else if (ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+ && ecs->ws.kind != TARGET_WAITKIND_EXITED)
+ set_executing (inferior_ptid, 0);
}
switch (infwait_state)
case TARGET_WAITKIND_EXITED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXITED\n");
+ inferior_ptid = ecs->ptid;
target_terminal_ours (); /* Must do this before mourn anyway */
print_stop_reason (EXITED, ecs->ws.value.integer);
case TARGET_WAITKIND_SIGNALLED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SIGNALLED\n");
+ inferior_ptid = ecs->ptid;
stop_print_frame = 0;
target_terminal_ours (); /* Must do this before mourn anyway */
}
}
- stepping_past_singlestep_breakpoint = 0;
-
if (!ptid_equal (deferred_step_ptid, null_ptid))
{
/* In non-stop mode, there's never a deferred_step_ptid set. */
the fact that we were supposed to switch back. */
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
{
- struct thread_info *tp;
-
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: handling deferred step\n");
SIGTRAP. Some systems (e.g. Windows), and stubs supporting
target extended-remote report it instead of a SIGSTOP
(e.g. gdbserver). We already rely on SIGTRAP being our
- signal, so this is no exception. */
+ signal, so this is no exception.
+
+ Also consider that the attach is complete when we see a
+ TARGET_SIGNAL_0. In non-stop mode, GDB will explicitly tell
+ the target to stop all threads of the inferior, in case the
+ low level attach operation doesn't stop them implicitly. If
+ they weren't stopped implicitly, then the stub will report a
+ TARGET_SIGNAL_0, meaning: stopped for no particular reason
+ other than GDB's request. */
if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
&& (ecs->event_thread->stop_signal == TARGET_SIGNAL_STOP
- || ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP))
+ || ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
+ || ecs->event_thread->stop_signal == TARGET_SIGNAL_0))
{
stop_stepping (ecs);
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
break;
case BPSTAT_WHAT_CHECK_SHLIBS:
- case BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK:
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_SHLIBS\n");
stop_stepping (ecs);
return;
}
-
- /* If we stopped due to an explicit catchpoint, then the
- (see above) call to SOLIB_ADD pulled in any symbols
- from a newly-loaded library, if appropriate.
-
- We do want the inferior to stop, but not where it is
- now, which is in the dynamic linker callback. Rather,
- we would like it stop in the user's program, just after
- the call that caused this catchpoint to trigger. That
- gives the user a more useful vantage from which to
- examine their program's state. */
- else if (what.main_action
- == BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK)
- {
- /* ??rehrauer: If I could figure out how to get the
- right return PC from here, we could just set a temp
- breakpoint and resume. I'm not sure we can without
- cracking open the dld's shared libraries and sniffing
- their unwind tables and text/data ranges, and that's
- not a terribly portable notion.
-
- Until that time, we must step the inferior out of the
- dld callback, and also out of the dld itself (and any
- code or stubs in libdld.sl, such as "shl_load" and
- friends) until we reach non-dld code. At that point,
- we can stop stepping. */
- bpstat_get_triggered_catchpoints (ecs->event_thread->stop_bpstat,
- &ecs->
- event_thread->
- stepping_through_solib_catchpoints);
- ecs->event_thread->stepping_through_solib_after_catch = 1;
-
- /* Be sure to lift all breakpoints, so the inferior does
- actually step past this point... */
- ecs->event_thread->stepping_over_breakpoint = 1;
- break;
- }
else
{
/* We want to step over this breakpoint, then keep going. */
test for stepping. But, if not stepping,
do not stop. */
+ /* In all-stop mode, if we're currently stepping but have stopped in
+ some other thread, we need to switch back to the stepped thread. */
+ if (!non_stop)
+ {
+ struct thread_info *tp;
+ tp = iterate_over_threads (currently_stepping_callback,
+ ecs->event_thread);
+ if (tp)
+ {
+ /* However, if the current thread is blocked on some internal
+ breakpoint, and we simply need to step over that breakpoint
+ to get it going again, do that first. */
+ if ((ecs->event_thread->trap_expected
+ && ecs->event_thread->stop_signal != TARGET_SIGNAL_TRAP)
+ || ecs->event_thread->stepping_over_breakpoint)
+ {
+ keep_going (ecs);
+ return;
+ }
+
+ /* Otherwise, we no longer expect a trap in the current thread.
+ Clear the trap_expected flag before switching back -- this is
+ what keep_going would do as well, if we called it. */
+ ecs->event_thread->trap_expected = 0;
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: switching back to stepped thread\n");
+
+ ecs->event_thread = tp;
+ ecs->ptid = tp->ptid;
+ context_switch (ecs->ptid);
+ keep_going (ecs);
+ return;
+ }
+ }
+
/* Are we stepping to get the inferior out of the dynamic linker's
hook (and possibly the dld itself) after catching a shlib
event? */
if (execution_direction == EXEC_REVERSE)
{
struct symtab_and_line sr_sal;
+
+ if (ecs->stop_func_start == 0
+ && in_solib_dynsym_resolve_code (stop_pc))
+ {
+ /* Stepped into runtime loader dynamic symbol
+ resolution code. Since we're in reverse,
+ we have already backed up through the runtime
+ loader and the dynamic function. This is just
+ the trampoline (jump table).
+
+ Just keep stepping, we'll soon be home.
+ */
+ keep_going (ecs);
+ return;
+ }
+ /* Normal (staticly linked) function call return. */
init_sal (&sr_sal);
sr_sal.pc = ecs->stop_func_start;
insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
/* Are we in the middle of stepping? */
+static int
+currently_stepping_thread (struct thread_info *tp)
+{
+ return (tp->step_range_end && tp->step_resume_breakpoint == NULL)
+ || tp->trap_expected
+ || tp->stepping_through_solib_after_catch;
+}
+
+static int
+currently_stepping_callback (struct thread_info *tp, void *data)
+{
+ /* Return true if any thread *but* the one passed in "data" is
+ in the middle of stepping. */
+ return tp != data && currently_stepping_thread (tp);
+}
+
static int
currently_stepping (struct thread_info *tp)
{
- return (((tp->step_range_end && tp->step_resume_breakpoint == NULL)
- || tp->trap_expected)
- || tp->stepping_through_solib_after_catch
- || bpstat_should_step ());
+ return currently_stepping_thread (tp) || bpstat_should_step ();
}
/* Inferior has stepped into a subroutine call with source code that
{
struct target_waitstatus last;
ptid_t last_ptid;
+ struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
get_last_target_status (&last_ptid, &last);
+ /* 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
+ here, so do this before any filtered output. */
+ if (target_has_execution)
+ {
+ if (!non_stop)
+ make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+ else if (last.kind != TARGET_WAITKIND_SIGNALLED
+ && last.kind != TARGET_WAITKIND_EXITED)
+ make_cleanup (finish_thread_state_cleanup, &inferior_ptid);
+ }
+
/* In non-stop mode, we don't want GDB to switch threads behind the
user's back, to avoid races where the user is typing a command to
apply to thread x, but GDB switches to thread y before the user
previous_inferior_ptid = inferior_ptid;
}
- /* NOTE drow/2004-01-17: Is this still necessary? */
- /* Make sure that the current_frame's pc is correct. This
- is a correction for setting up the frame info before doing
- gdbarch_decr_pc_after_break */
- if (target_has_execution)
- /* FIXME: cagney/2002-12-06: Has the PC changed? Thanks to
- gdbarch_decr_pc_after_break, the program counter can change. Ask the
- frame code to check for this and sort out any resultant mess.
- gdbarch_decr_pc_after_break needs to just go away. */
- deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
-
if (!breakpoints_always_inserted_mode () && target_has_execution)
{
if (remove_breakpoints ())
target_terminal_ours_for_output ();
printf_filtered (_("\
Cannot remove breakpoints because program is no longer writable.\n\
-It might be running in another process.\n\
Further execution is probably impossible.\n"));
}
}
/* 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 (target_has_stack && !stop_stack_dummy)
+ if (has_stack_frames () && !stop_stack_dummy)
set_current_sal_from_frame (get_current_frame (), 1);
- if (!target_has_stack)
+ /* Let the user/frontend see the threads as stopped. */
+ 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);
+
+ if (!has_stack_frames ())
goto done;
if (last.kind == TARGET_WAITKIND_SIGNALLED
internal_error (__FILE__, __LINE__, _("Unknown value."));
}
- if (ui_out_is_mi_like_p (uiout))
- {
-
- ui_out_field_int (uiout, "thread-id",
- pid_to_thread_id (inferior_ptid));
- if (non_stop)
- {
- struct cleanup *back_to = make_cleanup_ui_out_list_begin_end
- (uiout, "stopped-threads");
- ui_out_field_int (uiout, NULL,
- pid_to_thread_id (inferior_ptid));
- do_cleanups (back_to);
- }
- else
- ui_out_field_string (uiout, "stopped-threads", "all");
- }
/* The behavior of this routine with respect to the source
flag is:
SRC_LINE: Print only source line
if (stop_stack_dummy)
{
- /* Pop the empty frame that contains the stack dummy. POP_FRAME
- ends with a setting of the current frame, so we can use that
- next. */
- frame_pop (get_current_frame ());
- /* Set stop_pc to what it was before we called the function.
- Can't rely on restore_inferior_status because that only gets
- called if we don't stop in the called function. */
- stop_pc = read_pc ();
+ /* Pop the empty frame that contains the stack dummy.
+ This also restores inferior state prior to the call
+ (struct inferior_thread_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_inferior_status. 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:
annotate_stopped ();
- if (!suppress_stop_observer
- && !(target_has_execution
- && last.kind != TARGET_WAITKIND_SIGNALLED
- && last.kind != TARGET_WAITKIND_EXITED
- && inferior_thread ()->step_multi))
+
+ /* Suppress the stop observer if we're in the middle of:
+
+ - a step n (n > 1), as there still more steps to be done.
+
+ - a "finish" command, as the observer will be called in
+ finish_command_continuation, so it can include the inferior
+ function's return value.
+
+ - 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
+ || (!inferior_thread ()->step_multi
+ && !(inferior_thread ()->stop_bpstat
+ && inferior_thread ()->proceed_to_finish)
+ && !inferior_thread ()->in_infcall))
{
if (!ptid_equal (inferior_ptid, null_ptid))
- observer_notify_normal_stop (inferior_thread ()->stop_bpstat);
+ observer_notify_normal_stop (inferior_thread ()->stop_bpstat,
+ stop_print_frame);
else
- observer_notify_normal_stop (NULL);
+ observer_notify_normal_stop (NULL, stop_print_frame);
}
- if (target_has_execution
- && last.kind != TARGET_WAITKIND_SIGNALLED
- && last.kind != TARGET_WAITKIND_EXITED)
- {
- /* Delete the breakpoint we stopped at, if it wants to be deleted.
- Delete any breakpoint that is to be deleted at the next stop. */
- breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
- if (!non_stop)
- set_running (pid_to_ptid (-1), 0);
- else
- set_running (inferior_ptid, 0);
+ if (target_has_execution)
+ {
+ if (last.kind != TARGET_WAITKIND_SIGNALLED
+ && last.kind != TARGET_WAITKIND_EXITED)
+ /* Delete the breakpoint we stopped at, if it wants to be deleted.
+ Delete any breakpoint that is to be deleted at the next stop. */
+ breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
}
-
- /* 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);
-
}
static int
static void
sig_print_info (enum target_signal oursig)
{
- char *name = target_signal_to_name (oursig);
+ const char *name = target_signal_to_name (oursig);
int name_padding = 13 - strlen (name);
if (name_padding <= 0)
case TARGET_SIGNAL_INT:
if (!allsigs && !sigs[signum])
{
- if (query ("%s is used by the debugger.\n\
-Are you sure you want to change it? ", target_signal_to_name ((enum target_signal) signum)))
+ if (query (_("%s is used by the debugger.\n\
+Are you sure you want to change it? "), target_signal_to_name ((enum target_signal) signum)))
{
sigs[signum] = 1;
}
argv++;
}
- target_notice_signals (inferior_ptid);
+ for (signum = 0; signum < nsigs; signum++)
+ if (sigs[signum])
+ {
+ target_notice_signals (inferior_ptid);
- if (from_tty)
- {
- /* Show the results. */
- sig_print_header ();
- for (signum = 0; signum < nsigs; signum++)
- {
- if (sigs[signum])
- {
- sig_print_info (signum);
- }
- }
- }
+ if (from_tty)
+ {
+ /* Show the results. */
+ sig_print_header ();
+ for (; signum < nsigs; signum++)
+ if (sigs[signum])
+ sig_print_info (signum);
+ }
+
+ break;
+ }
do_cleanups (old_chain);
}
printf_filtered (_("\nUse the \"handle\" command to change these tables.\n"));
}
+
+/* The $_siginfo convenience variable is a bit special. We don't know
+ for sure the type of the value until we actually have a chance to
+ fetch the data. The type can change depending on gdbarch, so it it
+ also dependent on which thread you have selected.
+
+ 1. making $_siginfo be an internalvar that creates a new value on
+ access.
+
+ 2. making the value of $_siginfo be an lval_computed value. */
+
+/* This function implements the lval_computed support for reading a
+ $_siginfo value. */
+
+static void
+siginfo_value_read (struct value *v)
+{
+ LONGEST transferred;
+
+ transferred =
+ target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO,
+ NULL,
+ value_contents_all_raw (v),
+ value_offset (v),
+ TYPE_LENGTH (value_type (v)));
+
+ if (transferred != TYPE_LENGTH (value_type (v)))
+ error (_("Unable to read siginfo"));
+}
+
+/* This function implements the lval_computed support for writing a
+ $_siginfo value. */
+
+static void
+siginfo_value_write (struct value *v, struct value *fromval)
+{
+ LONGEST transferred;
+
+ transferred = target_write (¤t_target,
+ TARGET_OBJECT_SIGNAL_INFO,
+ NULL,
+ value_contents_all_raw (fromval),
+ value_offset (v),
+ TYPE_LENGTH (value_type (fromval)));
+
+ if (transferred != TYPE_LENGTH (value_type (fromval)))
+ error (_("Unable to write siginfo"));
+}
+
+static struct lval_funcs siginfo_value_funcs =
+ {
+ siginfo_value_read,
+ siginfo_value_write
+ };
+
+/* Return a new value with the correct type for the siginfo object of
+ the current thread. Return a void value if there's no object
+ available. */
+
+static struct value *
+siginfo_make_value (struct internalvar *var)
+{
+ struct type *type;
+ struct gdbarch *gdbarch;
+
+ if (target_has_stack
+ && !ptid_equal (inferior_ptid, null_ptid))
+ {
+ gdbarch = get_frame_arch (get_current_frame ());
+
+ if (gdbarch_get_siginfo_type_p (gdbarch))
+ {
+ type = gdbarch_get_siginfo_type (gdbarch);
+
+ return allocate_computed_value (type, &siginfo_value_funcs, NULL);
+ }
+ }
+
+ return allocate_value (builtin_type_void);
+}
+
\f
-struct inferior_status
+/* Inferior thread state.
+ These are details related to the inferior itself, and don't include
+ things like what frame the user had selected or what gdb was doing
+ with the target at the time.
+ For inferior function calls these are things we want to restore
+ regardless of whether the function call successfully completes
+ or the dummy frame has to be manually popped. */
+
+struct inferior_thread_state
{
enum target_signal stop_signal;
CORE_ADDR stop_pc;
+ struct regcache *registers;
+};
+
+struct inferior_thread_state *
+save_inferior_thread_state (void)
+{
+ struct inferior_thread_state *inf_state = XMALLOC (struct inferior_thread_state);
+ struct thread_info *tp = inferior_thread ();
+
+ inf_state->stop_signal = tp->stop_signal;
+ inf_state->stop_pc = stop_pc;
+
+ inf_state->registers = regcache_dup (get_current_regcache ());
+
+ return inf_state;
+}
+
+/* Restore inferior session state to INF_STATE. */
+
+void
+restore_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ struct thread_info *tp = inferior_thread ();
+
+ tp->stop_signal = inf_state->stop_signal;
+ stop_pc = inf_state->stop_pc;
+
+ /* The inferior can be gone if the user types "print exit(0)"
+ (and perhaps other times). */
+ if (target_has_execution)
+ /* NB: The register write goes through to the target. */
+ regcache_cpy (get_current_regcache (), inf_state->registers);
+ regcache_xfree (inf_state->registers);
+ xfree (inf_state);
+}
+
+static void
+do_restore_inferior_thread_state_cleanup (void *state)
+{
+ restore_inferior_thread_state (state);
+}
+
+struct cleanup *
+make_cleanup_restore_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ return make_cleanup (do_restore_inferior_thread_state_cleanup, inf_state);
+}
+
+void
+discard_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ regcache_xfree (inf_state->registers);
+ xfree (inf_state);
+}
+
+struct regcache *
+get_inferior_thread_state_regcache (struct inferior_thread_state *inf_state)
+{
+ return inf_state->registers;
+}
+
+/* Session related state for inferior function calls.
+ These are the additional bits of state that need to be restored
+ when an inferior function call successfully completes. */
+
+struct inferior_status
+{
bpstat stop_bpstat;
int stop_step;
int stop_stack_dummy;
int stop_after_trap;
int stop_soon;
- /* These are here because if call_function_by_hand has written some
- registers and then decides to call error(), we better not have changed
- any registers. */
- struct regcache *registers;
-
- /* A frame unique identifier. */
+ /* ID if the selected frame when the inferior function call was made. */
struct frame_id selected_frame_id;
- int breakpoint_proceeded;
- int restore_stack_info;
int proceed_to_finish;
+ int in_infcall;
};
-void
-write_inferior_status_register (struct inferior_status *inf_status, int regno,
- LONGEST val)
-{
- int size = register_size (current_gdbarch, regno);
- void *buf = alloca (size);
- store_signed_integer (buf, size, val);
- regcache_raw_write (inf_status->registers, regno, buf);
-}
-
/* Save all of the information associated with the inferior<==>gdb
- connection. INF_STATUS is a pointer to a "struct inferior_status"
- (defined in inferior.h). */
+ connection. */
struct inferior_status *
-save_inferior_status (int restore_stack_info)
+save_inferior_status (void)
{
struct inferior_status *inf_status = XMALLOC (struct inferior_status);
struct thread_info *tp = inferior_thread ();
struct inferior *inf = current_inferior ();
- inf_status->stop_signal = tp->stop_signal;
- inf_status->stop_pc = stop_pc;
inf_status->stop_step = tp->stop_step;
inf_status->stop_stack_dummy = stop_stack_dummy;
inf_status->stopped_by_random_signal = stopped_by_random_signal;
called. */
inf_status->stop_bpstat = tp->stop_bpstat;
tp->stop_bpstat = bpstat_copy (tp->stop_bpstat);
- inf_status->breakpoint_proceeded = breakpoint_proceeded;
- inf_status->restore_stack_info = restore_stack_info;
inf_status->proceed_to_finish = tp->proceed_to_finish;
-
- inf_status->registers = regcache_dup (get_current_regcache ());
+ inf_status->in_infcall = tp->in_infcall;
inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL));
+
return inf_status;
}
return (1);
}
+/* Restore inferior session state to INF_STATUS. */
+
void
restore_inferior_status (struct inferior_status *inf_status)
{
struct thread_info *tp = inferior_thread ();
struct inferior *inf = current_inferior ();
- tp->stop_signal = inf_status->stop_signal;
- stop_pc = inf_status->stop_pc;
tp->stop_step = inf_status->stop_step;
stop_stack_dummy = inf_status->stop_stack_dummy;
stopped_by_random_signal = inf_status->stopped_by_random_signal;
inf->stop_soon = inf_status->stop_soon;
bpstat_clear (&tp->stop_bpstat);
tp->stop_bpstat = inf_status->stop_bpstat;
- breakpoint_proceeded = inf_status->breakpoint_proceeded;
+ inf_status->stop_bpstat = NULL;
tp->proceed_to_finish = inf_status->proceed_to_finish;
+ tp->in_infcall = inf_status->in_infcall;
- /* The inferior can be gone if the user types "print exit(0)"
- (and perhaps other times). */
- if (target_has_execution)
- /* NB: The register write goes through to the target. */
- regcache_cpy (get_current_regcache (), inf_status->registers);
- regcache_xfree (inf_status->registers);
-
- /* FIXME: If we are being called after stopping in a function which
- is called from gdb, we should not be trying to restore the
- selected frame; it just prints a spurious error message (The
- message is useful, however, in detecting bugs in gdb (like if gdb
- clobbers the stack)). In fact, should we be restoring the
- inferior status at all in that case? . */
-
- if (target_has_stack && inf_status->restore_stack_info)
+ if (target_has_stack)
{
/* The point of catch_errors is that if the stack is clobbered,
walking the stack might encounter a garbage pointer and
/* Error in restoring the selected frame. Select the innermost
frame. */
select_frame (get_current_frame ());
-
}
xfree (inf_status);
{
/* See save_inferior_status for info on stop_bpstat. */
bpstat_clear (&inf_status->stop_bpstat);
- regcache_xfree (inf_status->registers);
xfree (inf_status);
}
-
+\f
int
inferior_has_forked (ptid_t pid, ptid_t *child_pid)
{
observer_attach_thread_ptid_changed (infrun_thread_ptid_changed);
observer_attach_thread_stop_requested (infrun_thread_stop_requested);
+ observer_attach_thread_exit (infrun_thread_thread_exit);
+
+ /* Explicitly create without lookup, since that tries to create a
+ value with a void typed value, and when we get here, gdbarch
+ isn't initialized yet. At this point, we're quite sure there
+ isn't another convenience variable of the same name. */
+ create_internalvar_type_lazy ("_siginfo", siginfo_make_value);
}