/* Low level interface to ptrace, for the remote server for GDB.
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2017 Free Software Foundation, Inc.
This file is part of GDB.
#include "agent.h"
#include "tdesc.h"
#include "rsp-low.h"
-
+#include "signals-state-save-restore.h"
#include "nat/linux-nat.h"
#include "nat/linux-waitpid.h"
#include "gdb_wait.h"
#include "tracepoint.h"
#include "hostio.h"
#include <inttypes.h>
+#include "common-inferior.h"
+#include "nat/fork-inferior.h"
+#include "environ.h"
#ifndef ELFMAG0
/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
then ELFMAG0 will have been defined. If it didn't get included by
return lwp->stop_reason;
}
+/* See nat/linux-nat.h. */
+
+int
+lwp_is_stepping (struct lwp_info *lwp)
+{
+ return lwp->stepping;
+}
+
/* A list of all unknown processes which receive stop signals. Some
other process will presumably claim each of these as forked
children momentarily. */
&& can_software_single_step ()
&& event == PTRACE_EVENT_VFORK)
{
- /* If we leave reinsert breakpoints there, child will
- hit it, so uninsert reinsert breakpoints from parent
+ /* If we leave single-step breakpoints there, child will
+ hit it, so uninsert single-step breakpoints from parent
(and child). Once vfork child is done, reinsert
them back to parent. */
- uninsert_reinsert_breakpoints (event_thr);
+ uninsert_single_step_breakpoints (event_thr);
}
clone_all_breakpoints (child_thr, event_thr);
event_lwp->status_pending_p = 1;
event_lwp->status_pending = wstat;
- /* If the parent thread is doing step-over with reinsert
- breakpoints, the list of reinsert breakpoints are cloned
+ /* Link the threads until the parent event is passed on to
+ higher layers. */
+ event_lwp->fork_relative = child_lwp;
+ child_lwp->fork_relative = event_lwp;
+
+ /* If the parent thread is doing step-over with single-step
+ breakpoints, the list of single-step breakpoints are cloned
from the parent's. Remove them from the child process.
In case of vfork, we'll reinsert them back once vforked
child is done. */
/* The child process is forked and stopped, so it is safe
to access its memory without stopping all other threads
from other processes. */
- delete_reinsert_breakpoints (child_thr);
+ delete_single_step_breakpoints (child_thr);
- gdb_assert (has_reinsert_breakpoints (event_thr));
- gdb_assert (!has_reinsert_breakpoints (child_thr));
+ gdb_assert (has_single_step_breakpoints (event_thr));
+ gdb_assert (!has_single_step_breakpoints (child_thr));
}
/* Report the event. */
if (event_lwp->bp_reinsert != 0 && can_software_single_step ())
{
- reinsert_reinsert_breakpoints (event_thr);
+ reinsert_single_step_breakpoints (event_thr);
- gdb_assert (has_reinsert_breakpoints (event_thr));
+ gdb_assert (has_single_step_breakpoints (event_thr));
}
/* Report the event. */
return lwp;
}
+/* Callback to be used when calling fork_inferior, responsible for
+ actually initiating the tracing of the inferior. */
+
+static void
+linux_ptrace_fun ()
+{
+ if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) 0) < 0)
+ trace_start_error_with_name ("ptrace");
+
+ if (setpgid (0, 0) < 0)
+ trace_start_error_with_name ("setpgid");
+
+ /* If GDBserver is connected to gdb via stdio, redirect the inferior's
+ stdout to stderr so that inferior i/o doesn't corrupt the connection.
+ Also, redirect stdin to /dev/null. */
+ if (remote_connection_is_stdio ())
+ {
+ if (close (0) < 0)
+ trace_start_error_with_name ("close");
+ if (open ("/dev/null", O_RDONLY) < 0)
+ trace_start_error_with_name ("open");
+ if (dup2 (2, 1) < 0)
+ trace_start_error_with_name ("dup2");
+ if (write (2, "stdin/stdout redirected\n",
+ sizeof ("stdin/stdout redirected\n") - 1) < 0)
+ {
+ /* Errors ignored. */;
+ }
+ }
+}
+
/* Start an inferior process and returns its pid.
- ALLARGS is a vector of program-name and args. */
+ PROGRAM is the name of the program to be started, and PROGRAM_ARGS
+ are its arguments. */
static int
-linux_create_inferior (char *program, char **allargs)
+linux_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
{
struct lwp_info *new_lwp;
int pid;
ptid_t ptid;
struct cleanup *restore_personality
= maybe_disable_address_space_randomization (disable_randomization);
+ std::string str_program_args = stringify_argv (program_args);
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
- pid = vfork ();
-#else
- pid = fork ();
-#endif
- if (pid < 0)
- perror_with_name ("fork");
-
- if (pid == 0)
- {
- close_most_fds ();
- ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
-
- setpgid (0, 0);
-
- /* If gdbserver is connected to gdb via stdio, redirect the inferior's
- stdout to stderr so that inferior i/o doesn't corrupt the connection.
- Also, redirect stdin to /dev/null. */
- if (remote_connection_is_stdio ())
- {
- close (0);
- open ("/dev/null", O_RDONLY);
- dup2 (2, 1);
- if (write (2, "stdin/stdout redirected\n",
- sizeof ("stdin/stdout redirected\n") - 1) < 0)
- {
- /* Errors ignored. */;
- }
- }
-
- execv (program, allargs);
- if (errno == ENOENT)
- execvp (program, allargs);
-
- fprintf (stderr, "Cannot exec %s: %s.\n", program,
- strerror (errno));
- fflush (stderr);
- _exit (0177);
- }
+ pid = fork_inferior (program,
+ str_program_args.c_str (),
+ environ_vector (get_environ ()), linux_ptrace_fun,
+ NULL, NULL, NULL, NULL);
do_cleanups (restore_personality);
new_lwp = add_lwp (ptid);
new_lwp->must_set_ptrace_flags = 1;
+ post_fork_inferior (pid, program);
+
return pid;
}
thread execs). */
if (debug_threads)
- fprintf (stderr,
- "CZL: Thread group leader %d zombie "
- "(it exited, or another thread execd).\n",
- leader_pid);
+ debug_printf ("CZL: Thread group leader %d zombie "
+ "(it exited, or another thread execd).\n",
+ leader_pid);
delete_lwp (leader_lp);
}
return 1;
else
{
- /* GDBserver must insert reinsert breakpoint for software
+ /* GDBserver must insert single-step breakpoint for software
single step. */
- gdb_assert (has_reinsert_breakpoints (thread));
+ gdb_assert (has_single_step_breakpoints (thread));
return 0;
}
}
&& !lp->status_pending_p
&& thread->last_status.kind == TARGET_WAITKIND_IGNORE)
{
- int step = thread->last_resume_kind == resume_step;
+ int step = 0;
+
+ if (thread->last_resume_kind == resume_step)
+ step = maybe_hw_step (thread);
if (debug_threads)
debug_printf ("RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
if (ptid_equal (filter_ptid, minus_one_ptid) || ptid_is_pid (filter_ptid))
{
event_thread = (struct thread_info *)
- find_inferior (&all_threads, status_pending_p_callback, &filter_ptid);
+ find_inferior_in_random (&all_threads, status_pending_p_callback,
+ &filter_ptid);
if (event_thread != NULL)
event_child = get_thread_lwp (event_thread);
if (debug_threads && event_thread)
/* ... and find an LWP with a status to report to the core, if
any. */
event_thread = (struct thread_info *)
- find_inferior (&all_threads, status_pending_p_callback, &filter_ptid);
+ find_inferior_in_random (&all_threads, status_pending_p_callback,
+ &filter_ptid);
if (event_thread != NULL)
{
event_child = get_thread_lwp (event_thread);
the breakpoint address.
So in the case of the hardware single step advance the PC manually
past the breakpoint and in the case of software single step advance only
- if it's not the reinsert_breakpoint we are hitting.
+ if it's not the single_step_breakpoint we are hitting.
This avoids that a program would keep trapping a permanent breakpoint
forever. */
if (!ptid_equal (step_over_bkpt, null_ptid)
&& event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
&& (event_child->stepping
- || !reinsert_breakpoint_inserted_here (event_child->stop_pc)))
+ || !single_step_breakpoint_inserted_here (event_child->stop_pc)))
{
int increment_pc = 0;
int breakpoint_kind = 0;
/* We have a SIGTRAP, possibly a step-over dance has just
finished. If so, tweak the state machine accordingly,
- reinsert breakpoints and delete any reinsert (software
- single-step) breakpoints. */
+ reinsert breakpoints and delete any single-step
+ breakpoints. */
step_over_finished = finish_step_over (event_child);
/* Now invoke the callbacks of any internal breakpoints there. */
linux_resume_one_lwp (event_child, 0, 0, NULL);
+ if (debug_threads)
+ debug_exit ();
return ignore_event (ourstatus);
}
}
linux_resume_one_lwp (event_child, event_child->stepping,
0, NULL);
+
+ if (debug_threads)
+ debug_exit ();
return ignore_event (ourstatus);
}
linux_resume_one_lwp (event_child, event_child->stepping,
WSTOPSIG (w), info_p);
}
+
+ if (debug_threads)
+ debug_exit ();
+
return ignore_event (ourstatus);
}
(*the_low_target.set_pc) (regcache, event_child->stop_pc);
}
- /* We may have finished stepping over a breakpoint. If so,
- we've stopped and suspended all LWPs momentarily except the
- stepping one. This is where we resume them all again. We're
- going to keep waiting, so use proceed, which handles stepping
- over the next breakpoint. */
+ if (step_over_finished)
+ {
+ /* If we have finished stepping over a breakpoint, we've
+ stopped and suspended all LWPs momentarily except the
+ stepping one. This is where we resume them all again.
+ We're going to keep waiting, so use proceed, which
+ handles stepping over the next breakpoint. */
+ unsuspend_all_lwps (event_child);
+ }
+ else
+ {
+ /* Remove the single-step breakpoints if any. Note that
+ there isn't single-step breakpoint if we finished stepping
+ over. */
+ if (can_software_single_step ()
+ && has_single_step_breakpoints (current_thread))
+ {
+ stop_all_lwps (0, event_child);
+ delete_single_step_breakpoints (current_thread);
+ unstop_all_lwps (0, event_child);
+ }
+ }
+
if (debug_threads)
debug_printf ("proceeding all threads.\n");
+ proceed_all_lwps ();
- if (step_over_finished)
- unsuspend_all_lwps (event_child);
+ if (debug_threads)
+ debug_exit ();
- proceed_all_lwps ();
return ignore_event (ourstatus);
}
/* Alright, we're going to report a stop. */
- if (!stabilizing_threads)
+ /* Remove single-step breakpoints. */
+ if (can_software_single_step ())
{
- /* In all-stop, stop all threads. */
- if (!non_stop)
- stop_all_lwps (0, NULL);
+ /* Remove single-step breakpoints or not. It it is true, stop all
+ lwps, so that other threads won't hit the breakpoint in the
+ staled memory. */
+ int remove_single_step_breakpoints_p = 0;
- /* If we're not waiting for a specific LWP, choose an event LWP
- from among those that have had events. Giving equal priority
- to all LWPs that have had events helps prevent
- starvation. */
- if (ptid_equal (ptid, minus_one_ptid))
+ if (non_stop)
{
- event_child->status_pending_p = 1;
- event_child->status_pending = w;
+ remove_single_step_breakpoints_p
+ = has_single_step_breakpoints (current_thread);
+ }
+ else
+ {
+ /* In all-stop, a stop reply cancels all previous resume
+ requests. Delete all single-step breakpoints. */
+ struct inferior_list_entry *inf, *tmp;
- select_event_lwp (&event_child);
+ ALL_INFERIORS (&all_threads, inf, tmp)
+ {
+ struct thread_info *thread = (struct thread_info *) inf;
- /* current_thread and event_child must stay in sync. */
- current_thread = get_lwp_thread (event_child);
+ if (has_single_step_breakpoints (thread))
+ {
+ remove_single_step_breakpoints_p = 1;
+ break;
+ }
+ }
+ }
- event_child->status_pending_p = 0;
- w = event_child->status_pending;
+ if (remove_single_step_breakpoints_p)
+ {
+ /* If we remove single-step breakpoints from memory, stop all lwps,
+ so that other threads won't hit the breakpoint in the staled
+ memory. */
+ stop_all_lwps (0, event_child);
+
+ if (non_stop)
+ {
+ gdb_assert (has_single_step_breakpoints (current_thread));
+ delete_single_step_breakpoints (current_thread);
+ }
+ else
+ {
+ struct inferior_list_entry *inf, *tmp;
+
+ ALL_INFERIORS (&all_threads, inf, tmp)
+ {
+ struct thread_info *thread = (struct thread_info *) inf;
+
+ if (has_single_step_breakpoints (thread))
+ delete_single_step_breakpoints (thread);
+ }
+ }
+
+ unstop_all_lwps (0, event_child);
}
+ }
+
+ if (!stabilizing_threads)
+ {
+ /* In all-stop, stop all threads. */
+ if (!non_stop)
+ stop_all_lwps (0, NULL);
if (step_over_finished)
{
}
}
+ /* If we're not waiting for a specific LWP, choose an event LWP
+ from among those that have had events. Giving equal priority
+ to all LWPs that have had events helps prevent
+ starvation. */
+ if (ptid_equal (ptid, minus_one_ptid))
+ {
+ event_child->status_pending_p = 1;
+ event_child->status_pending = w;
+
+ select_event_lwp (&event_child);
+
+ /* current_thread and event_child must stay in sync. */
+ current_thread = get_lwp_thread (event_child);
+
+ event_child->status_pending_p = 0;
+ w = event_child->status_pending;
+ }
+
+
/* Stabilize threads (move out of jump pads). */
if (!non_stop)
stabilize_threads ();
{
/* If the reported event is an exit, fork, vfork or exec, let
GDB know. */
+
+ /* Break the unreported fork relationship chain. */
+ if (event_child->waitstatus.kind == TARGET_WAITKIND_FORKED
+ || event_child->waitstatus.kind == TARGET_WAITKIND_VFORKED)
+ {
+ event_child->fork_relative->fork_relative = NULL;
+ event_child->fork_relative = NULL;
+ }
+
*ourstatus = event_child->waitstatus;
/* Clear the event lwp's waitstatus since we handled it already. */
event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
static void
install_software_single_step_breakpoints (struct lwp_info *lwp)
{
- int i;
- CORE_ADDR pc;
struct thread_info *thread = get_lwp_thread (lwp);
struct regcache *regcache = get_thread_regcache (thread, 1);
- VEC (CORE_ADDR) *next_pcs = NULL;
struct cleanup *old_chain = make_cleanup_restore_current_thread ();
- make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
-
current_thread = thread;
- next_pcs = (*the_low_target.get_next_pcs) (regcache);
+ std::vector<CORE_ADDR> next_pcs = the_low_target.get_next_pcs (regcache);
- for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); ++i)
- set_reinsert_breakpoint (pc, current_ptid);
+ for (CORE_ADDR pc : next_pcs)
+ set_single_step_breakpoint (pc, current_ptid);
do_cleanups (old_chain);
}
if (fast_tp_collecting == 0)
{
if (step == 0)
- fprintf (stderr, "BAD - reinserting but not stepping.\n");
+ warning ("BAD - reinserting but not stepping.");
if (lwp->suspended)
- fprintf (stderr, "BAD - reinserting and suspended(%d).\n",
- lwp->suspended);
+ warning ("BAD - reinserting and suspended(%d).",
+ lwp->suspended);
}
}
step = maybe_hw_step (thread);
}
- else
- {
- /* If the thread isn't doing step-over, there shouldn't be any
- reinsert breakpoints. */
- gdb_assert (!has_reinsert_breakpoints (thread));
- }
if (fast_tp_collecting == 1)
{
continue;
}
+ /* Ignore (wildcard) resume requests for already-resumed
+ threads. */
+ if (r->resume[ndx].kind != resume_stop
+ && thread->last_resume_kind != resume_stop)
+ {
+ if (debug_threads)
+ debug_printf ("already %s LWP %ld at GDB's request\n",
+ (thread->last_resume_kind
+ == resume_step)
+ ? "stepping"
+ : "continuing",
+ lwpid_of (thread));
+ continue;
+ }
+
+ /* Don't let wildcard resumes resume fork children that GDB
+ does not yet know are new fork children. */
+ if (lwp->fork_relative != NULL)
+ {
+ struct inferior_list_entry *inf, *tmp;
+ struct lwp_info *rel = lwp->fork_relative;
+
+ if (rel->status_pending_p
+ && (rel->waitstatus.kind == TARGET_WAITKIND_FORKED
+ || rel->waitstatus.kind == TARGET_WAITKIND_VFORKED))
+ {
+ if (debug_threads)
+ debug_printf ("not resuming LWP %ld: has queued stop reply\n",
+ lwpid_of (thread));
+ continue;
+ }
+ }
+
+ /* If the thread has a pending event that has already been
+ reported to GDBserver core, but GDB has not pulled the
+ event out of the vStopped queue yet, likewise, ignore the
+ (wildcard) resume request. */
+ if (in_queued_stop_replies (entry->id))
+ {
+ if (debug_threads)
+ debug_printf ("not resuming LWP %ld: has queued stop reply\n",
+ lwpid_of (thread));
+ continue;
+ }
+
lwp->resume = &r->resume[ndx];
thread->last_resume_kind = lwp->resume->kind;
}
/* Finish a step-over. Reinsert the breakpoint we had uninserted in
- start_step_over, if still there, and delete any reinsert
+ start_step_over, if still there, and delete any single-step
breakpoints we've set, on non hardware single-step targets. */
static int
lwp->bp_reinsert = 0;
- /* Delete any software-single-step reinsert breakpoints. No
- longer needed. We don't have to worry about other threads
- hitting this trap, and later not being able to explain it,
- because we were stepping over a breakpoint, and we hold all
- threads but LWP stopped while doing that. */
+ /* Delete any single-step breakpoints. No longer needed. We
+ don't have to worry about other threads hitting this trap,
+ and later not being able to explain it, because we were
+ stepping over a breakpoint, and we hold all threads but
+ LWP stopped while doing that. */
if (!can_hardware_single_step ())
{
- gdb_assert (has_reinsert_breakpoints (current_thread));
- delete_reinsert_breakpoints (current_thread);
+ gdb_assert (has_single_step_breakpoints (current_thread));
+ delete_single_step_breakpoints (current_thread);
}
step_over_bkpt = null_ptid;
if (debug_threads)
debug_printf (" stepping LWP %ld, client wants it stepping\n",
lwpid_of (thread));
- step = 1;
+
+ /* If resume_step is requested by GDB, install single-step
+ breakpoints when the thread is about to be actually resumed if
+ the single-step breakpoints weren't removed. */
+ if (can_software_single_step ()
+ && !has_single_step_breakpoints (thread))
+ install_software_single_step_breakpoints (lwp);
+
+ step = maybe_hw_step (thread);
}
else if (lwp->bp_reinsert != 0)
{
not "active". This can happen in normal operation,
so suppress the warning in this case. */
}
+ else if (errno == ESRCH)
+ {
+ /* At this point, ESRCH should mean the process is
+ already gone, in which case we simply ignore attempts
+ to read its registers. */
+ }
else
{
char s[256];
static void
linux_request_interrupt (void)
{
- extern unsigned long signal_pid;
-
/* Send a SIGINT to the process group. This acts just like the user
typed a ^C on the controlling terminal. */
kill (-signal_pid, SIGINT);
static int
linux_supports_range_stepping (void)
{
+ if (can_software_single_step ())
+ return 1;
if (*the_low_target.supports_range_stepping == NULL)
return 0;