#include "linux-low.h"
#include "nat/linux-osdata.h"
#include "agent.h"
+#include "tdesc.h"
+#include "rsp-low.h"
#include "nat/linux-nat.h"
#include "nat/linux-waitpid.h"
#include "gdb_wait.h"
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include "nat/linux-ptrace.h"
#include "nat/linux-procfs.h"
+#include "nat/linux-personality.h"
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
definition of elf_fpregset_t. */
#include <elf.h>
#endif
+#include "nat/linux-namespaces.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
#define O_LARGEFILE 0
#endif
-#ifndef W_STOPCODE
-#define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
-#endif
-
-/* This is the kernel's hard limit. Not to be confused with
- SIGRTMIN. */
-#ifndef __SIGRTMIN
-#define __SIGRTMIN 32
-#endif
-
/* Some targets did not define these ptrace constants from the start,
so gdbserver defines them locally here. In the future, these may
be removed after they are added to asm/ptrace.h. */
#ifdef HAVE_LINUX_BTRACE
# include "nat/linux-btrace.h"
+# include "btrace-common.h"
#endif
#ifndef HAVE_ELF32_AUXV_T
} Elf64_auxv_t;
#endif
+/* Does the current host support PTRACE_GETREGSET? */
+int have_ptrace_getregset = -1;
+
+/* LWP accessors. */
+
+/* See nat/linux-nat.h. */
+
+ptid_t
+ptid_of_lwp (struct lwp_info *lwp)
+{
+ return ptid_of (get_lwp_thread (lwp));
+}
+
+/* See nat/linux-nat.h. */
+
+void
+lwp_set_arch_private_info (struct lwp_info *lwp,
+ struct arch_lwp_info *info)
+{
+ lwp->arch_private = info;
+}
+
+/* See nat/linux-nat.h. */
+
+struct arch_lwp_info *
+lwp_arch_private_info (struct lwp_info *lwp)
+{
+ return lwp->arch_private;
+}
+
+/* See nat/linux-nat.h. */
+
+int
+lwp_is_stopped (struct lwp_info *lwp)
+{
+ return lwp->stopped;
+}
+
+/* See nat/linux-nat.h. */
+
+enum target_stop_reason
+lwp_stop_reason (struct lwp_info *lwp)
+{
+ return lwp->stop_reason;
+}
+
/* A list of all unknown processes which receive stop signals. Some
other process will presumably claim each of these as forked
children momentarily. */
static void
add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
{
- struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
+ struct simple_pid_list *new_pid = XNEW (struct simple_pid_list);
new_pid->pid = pid;
new_pid->status = status;
int *wstat, int options);
static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
static struct lwp_info *add_lwp (ptid_t ptid);
+static void linux_mourn (struct process_info *process);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int lwp_is_marked_dead (struct lwp_info *lwp);
static void proceed_all_lwps (void);
static int finish_step_over (struct lwp_info *lwp);
-static CORE_ADDR get_stop_pc (struct lwp_info *lwp);
static int kill_lwp (unsigned long lwpid, int signo);
+static void enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info);
+static void complete_ongoing_step_over (void);
+
+/* When the event-loop is doing a step-over, this points at the thread
+ being stepped. */
+ptid_t step_over_bkpt;
/* True if the low target can hardware single-step. Such targets
don't need a BREAKPOINT_REINSERT_ADDR callback. */
struct process_info *proc;
proc = add_process (pid, attached);
- proc->private = xcalloc (1, sizeof (*proc->private));
-
- /* Set the arch when the first LWP stops. */
- proc->private->new_inferior = 1;
+ proc->priv = XCNEW (struct process_info_private);
if (the_low_target.new_process != NULL)
- proc->private->arch_private = the_low_target.new_process ();
+ proc->priv->arch_private = the_low_target.new_process ();
return proc;
}
-/* Handle a GNU/Linux extended wait response. If we see a clone
- event, we need to add the new LWP to our list (and not report the
- trap to higher layers). */
+static CORE_ADDR get_pc (struct lwp_info *lwp);
+
+/* Implement the arch_setup target_ops method. */
static void
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+linux_arch_setup (void)
{
+ the_low_target.arch_setup ();
+}
+
+/* Call the target arch_setup function on THREAD. */
+
+static void
+linux_arch_setup_thread (struct thread_info *thread)
+{
+ struct thread_info *saved_thread;
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ linux_arch_setup ();
+
+ current_thread = saved_thread;
+}
+
+/* Handle a GNU/Linux extended wait response. If we see a clone,
+ fork, or vfork event, we need to add the new LWP to our list
+ (and return 0 so as not to report the trap to higher layers).
+ If we see an exec event, we will modify ORIG_EVENT_LWP to point
+ to a new LWP representing the new program. */
+
+static int
+handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
+{
+ struct lwp_info *event_lwp = *orig_event_lwp;
int event = linux_ptrace_get_extended_event (wstat);
- struct thread_info *event_thr = get_lwp_thread (event_child);
+ struct thread_info *event_thr = get_lwp_thread (event_lwp);
struct lwp_info *new_lwp;
- if (event == PTRACE_EVENT_CLONE)
+ gdb_assert (event_lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
+
+ if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+ || (event == PTRACE_EVENT_CLONE))
{
ptid_t ptid;
unsigned long new_pid;
int ret, status;
+ /* Get the pid of the new lwp. */
ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
&new_pid);
warning ("wait returned unexpected status 0x%x", status);
}
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
+ {
+ struct process_info *parent_proc;
+ struct process_info *child_proc;
+ struct lwp_info *child_lwp;
+ struct thread_info *child_thr;
+ struct target_desc *tdesc;
+
+ ptid = ptid_build (new_pid, new_pid, 0);
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got fork event from LWP %ld, "
+ "new child is %d\n",
+ ptid_get_lwp (ptid_of (event_thr)),
+ ptid_get_pid (ptid));
+ }
+
+ /* Add the new process to the tables and clone the breakpoint
+ lists of the parent. We need to do this even if the new process
+ will be detached, since we will need the process object and the
+ breakpoints to remove any breakpoints from memory when we
+ detach, and the client side will access registers. */
+ child_proc = linux_add_process (new_pid, 0);
+ gdb_assert (child_proc != NULL);
+ child_lwp = add_lwp (ptid);
+ gdb_assert (child_lwp != NULL);
+ child_lwp->stopped = 1;
+ child_lwp->must_set_ptrace_flags = 1;
+ child_lwp->status_pending_p = 0;
+ child_thr = get_lwp_thread (child_lwp);
+ child_thr->last_resume_kind = resume_stop;
+ child_thr->last_status.kind = TARGET_WAITKIND_STOPPED;
+
+ /* If we're suspending all threads, leave this one suspended
+ too. */
+ if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS)
+ {
+ if (debug_threads)
+ debug_printf ("HEW: leaving child suspended\n");
+ child_lwp->suspended = 1;
+ }
+
+ parent_proc = get_thread_process (event_thr);
+ child_proc->attached = parent_proc->attached;
+ clone_all_breakpoints (&child_proc->breakpoints,
+ &child_proc->raw_breakpoints,
+ parent_proc->breakpoints);
+
+ tdesc = XNEW (struct target_desc);
+ copy_target_description (tdesc, parent_proc->tdesc);
+ child_proc->tdesc = tdesc;
+
+ /* Clone arch-specific process data. */
+ if (the_low_target.new_fork != NULL)
+ the_low_target.new_fork (parent_proc, child_proc);
+
+ /* Save fork info in the parent thread. */
+ if (event == PTRACE_EVENT_FORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ else if (event == PTRACE_EVENT_VFORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
+ event_lwp->waitstatus.value.related_pid = ptid;
+
+ /* The status_pending field contains bits denoting the
+ extended event, so when the pending event is handled,
+ the handler will look at lwp->waitstatus. */
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+
+ /* Report the event. */
+ return 0;
+ }
+
if (debug_threads)
debug_printf ("HEW: Got clone event "
"from LWP %ld, new child is LWP %ld\n",
/* Normally we will get the pending SIGSTOP. But in some cases
we might get another signal delivered to the group first.
If we do get another signal, be sure not to lose it. */
- if (WSTOPSIG (status) == SIGSTOP)
+ if (WSTOPSIG (status) != SIGSTOP)
{
- if (stopping_threads != NOT_STOPPING_THREADS)
- new_lwp->stop_pc = get_stop_pc (new_lwp);
- else
- linux_resume_one_lwp (new_lwp, 0, 0, NULL);
+ new_lwp->stop_expected = 1;
+ new_lwp->status_pending_p = 1;
+ new_lwp->status_pending = status;
}
- else
+ else if (report_thread_events)
{
- new_lwp->stop_expected = 1;
+ new_lwp->waitstatus.kind = TARGET_WAITKIND_THREAD_CREATED;
+ new_lwp->status_pending_p = 1;
+ new_lwp->status_pending = status;
+ }
- if (stopping_threads != NOT_STOPPING_THREADS)
- {
- new_lwp->stop_pc = get_stop_pc (new_lwp);
- new_lwp->status_pending_p = 1;
- new_lwp->status_pending = status;
- }
- else
- /* Pass the signal on. This is what GDB does - except
- shouldn't we really report it instead? */
- linux_resume_one_lwp (new_lwp, 0, WSTOPSIG (status), NULL);
+ /* Don't report the event. */
+ return 1;
+ }
+ else if (event == PTRACE_EVENT_VFORK_DONE)
+ {
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+
+ /* Report the event. */
+ return 0;
+ }
+ else if (event == PTRACE_EVENT_EXEC && report_exec_events)
+ {
+ struct process_info *proc;
+ ptid_t event_ptid;
+ pid_t event_pid;
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
}
- /* Always resume the current thread. If we are stopping
- threads, it will have a pending SIGSTOP; we may as well
- collect it now. */
- linux_resume_one_lwp (event_child, event_child->stepping, 0, NULL);
+ /* Get the event ptid. */
+ event_ptid = ptid_of (event_thr);
+ event_pid = ptid_get_pid (event_ptid);
+
+ /* Delete the execing process and all its threads. */
+ proc = get_thread_process (event_thr);
+ linux_mourn (proc);
+ current_thread = NULL;
+
+ /* Create a new process/lwp/thread. */
+ proc = linux_add_process (event_pid, 0);
+ event_lwp = add_lwp (event_ptid);
+ event_thr = get_lwp_thread (event_lwp);
+ gdb_assert (current_thread == event_thr);
+ linux_arch_setup_thread (event_thr);
+
+ /* Set the event status. */
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_lwp->waitstatus.value.execd_pathname
+ = xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr)));
+
+ /* Mark the exec status as pending. */
+ event_lwp->stopped = 1;
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+ event_thr->last_resume_kind = resume_continue;
+ event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
+
+ /* Report the event. */
+ *orig_event_lwp = event_lwp;
+ return 0;
}
+
+ internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
/* Return the PC as read from the regcache of LWP, without any
The SIGTRAP could mean several things.
On i386, where decr_pc_after_break is non-zero:
- If we were single-stepping this process using PTRACE_SINGLESTEP,
- we will get only the one SIGTRAP (even if the instruction we
- stepped over was a breakpoint). The value of $eip will be the
- next instruction.
+
+ If we were single-stepping this process using PTRACE_SINGLESTEP, we
+ will get only the one SIGTRAP. The value of $eip will be the next
+ instruction. If the instruction we stepped over was a breakpoint,
+ we need to decrement the PC.
+
If we continue the process using PTRACE_CONT, we will get a
SIGTRAP when we hit a breakpoint. The value of $eip will be
the instruction after the breakpoint (i.e. needs to be
decremented). If we report the SIGTRAP to GDB, we must also
- report the undecremented PC. If we cancel the SIGTRAP, we
+ report the undecremented PC. If the breakpoint is removed, we
must resume at the decremented PC.
- (Presumably, not yet tested) On a non-decr_pc_after_break machine
- with hardware or kernel single-step:
- If we single-step over a breakpoint instruction, our PC will
- point at the following instruction. If we continue and hit a
- breakpoint instruction, our PC will point at the breakpoint
+ On a non-decr_pc_after_break machine with hardware or kernel
+ single-step:
+
+ If we either single-step a breakpoint instruction, or continue and
+ hit a breakpoint instruction, our PC will point at the breakpoint
instruction. */
-static CORE_ADDR
-get_stop_pc (struct lwp_info *lwp)
+static int
+check_stopped_by_breakpoint (struct lwp_info *lwp)
{
- CORE_ADDR stop_pc;
+ CORE_ADDR pc;
+ CORE_ADDR sw_breakpoint_pc;
+ struct thread_info *saved_thread;
+#if USE_SIGTRAP_SIGINFO
+ siginfo_t siginfo;
+#endif
if (the_low_target.get_pc == NULL)
return 0;
- stop_pc = get_pc (lwp);
+ pc = get_pc (lwp);
+ sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break;
- if (WSTOPSIG (lwp->last_status) == SIGTRAP
- && !lwp->stepping
- && !lwp->stopped_by_watchpoint
- && !linux_is_extended_waitstatus (lwp->last_status))
- stop_pc -= the_low_target.decr_pc_after_break;
+ /* breakpoint_at reads from the current thread. */
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (lwp);
- if (debug_threads)
- debug_printf ("stop pc is 0x%lx\n", (long) stop_pc);
+#if USE_SIGTRAP_SIGINFO
+ if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
+ (PTRACE_TYPE_ARG3) 0, &siginfo) == 0)
+ {
+ if (siginfo.si_signo == SIGTRAP)
+ {
+ if (GDB_ARCH_IS_TRAP_BRKPT (siginfo.si_code))
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by software breakpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ /* Back up the PC if necessary. */
+ if (pc != sw_breakpoint_pc)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, sw_breakpoint_pc);
+ }
+
+ lwp->stop_pc = sw_breakpoint_pc;
+ lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+ current_thread = saved_thread;
+ return 1;
+ }
+ else if (siginfo.si_code == TRAP_HWBKPT)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by hardware "
+ "breakpoint/watchpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ lwp->stop_pc = pc;
+ lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+ current_thread = saved_thread;
+ return 1;
+ }
+ else if (siginfo.si_code == TRAP_TRACE)
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by trace\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ lwp->stop_reason = TARGET_STOPPED_BY_SINGLE_STEP;
+ }
+ }
+ }
+#else
+ /* We may have just stepped a breakpoint instruction. E.g., in
+ non-stop mode, GDB first tells the thread A to step a range, and
+ then the user inserts a breakpoint inside the range. In that
+ case we need to report the breakpoint PC. */
+ if ((!lwp->stepping || lwp->stop_pc == sw_breakpoint_pc)
+ && (*the_low_target.breakpoint_at) (sw_breakpoint_pc))
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by software breakpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ /* Back up the PC if necessary. */
+ if (pc != sw_breakpoint_pc)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, sw_breakpoint_pc);
+ }
+
+ lwp->stop_pc = sw_breakpoint_pc;
+ lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+ current_thread = saved_thread;
+ return 1;
+ }
+
+ if (hardware_breakpoint_inserted_here (pc))
+ {
+ if (debug_threads)
+ {
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ debug_printf ("CSBB: %s stopped by hardware breakpoint\n",
+ target_pid_to_str (ptid_of (thr)));
+ }
+
+ lwp->stop_pc = pc;
+ lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+ current_thread = saved_thread;
+ return 1;
+ }
+#endif
- return stop_pc;
+ current_thread = saved_thread;
+ return 0;
}
static struct lwp_info *
{
struct lwp_info *lwp;
- lwp = (struct lwp_info *) xmalloc (sizeof (*lwp));
- memset (lwp, 0, sizeof (*lwp));
+ lwp = XCNEW (struct lwp_info);
+
+ lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
if (the_low_target.new_thread != NULL)
- lwp->arch_private = the_low_target.new_thread ();
+ the_low_target.new_thread (lwp);
lwp->thread = add_thread (ptid, lwp);
static int
linux_create_inferior (char *program, char **allargs)
{
-#ifdef HAVE_PERSONALITY
- int personality_orig = 0, personality_set = 0;
-#endif
struct lwp_info *new_lwp;
int pid;
ptid_t ptid;
-
-#ifdef HAVE_PERSONALITY
- if (disable_randomization)
- {
- errno = 0;
- personality_orig = personality (0xffffffff);
- if (errno == 0 && !(personality_orig & ADDR_NO_RANDOMIZE))
- {
- personality_set = 1;
- personality (personality_orig | ADDR_NO_RANDOMIZE);
- }
- if (errno != 0 || (personality_set
- && !(personality (0xffffffff) & ADDR_NO_RANDOMIZE)))
- warning ("Error disabling address space randomization: %s",
- strerror (errno));
- }
-#endif
+ struct cleanup *restore_personality
+ = maybe_disable_address_space_randomization (disable_randomization);
#if defined(__UCLIBC__) && defined(HAS_NOMMU)
pid = vfork ();
_exit (0177);
}
-#ifdef HAVE_PERSONALITY
- if (personality_set)
- {
- errno = 0;
- personality (personality_orig);
- if (errno != 0)
- warning ("Error restoring address space randomization: %s",
- strerror (errno));
- }
-#endif
+ do_cleanups (restore_personality);
linux_add_process (pid, 0);
return pid;
}
-char *
-linux_attach_fail_reason_string (ptid_t ptid, int err)
-{
- static char *reason_string;
- struct buffer buffer;
- char *warnings;
- long lwpid = ptid_get_lwp (ptid);
-
- xfree (reason_string);
-
- buffer_init (&buffer);
- linux_ptrace_attach_fail_reason (lwpid, &buffer);
- buffer_grow_str0 (&buffer, "");
- warnings = buffer_finish (&buffer);
- if (warnings[0] != '\0')
- reason_string = xstrprintf ("%s (%d), %s",
- strerror (err), err, warnings);
- else
- reason_string = xstrprintf ("%s (%d)",
- strerror (err), err);
- xfree (warnings);
- return reason_string;
-}
-
-/* Attach to an inferior process. */
+/* Attach to an inferior process. Returns 0 on success, ERRNO on
+ error. */
int
linux_attach_lwp (ptid_t ptid)
return 0;
}
+/* Callback for linux_proc_attach_tgid_threads. Attach to PTID if not
+ already attached. Returns true if a new LWP is found, false
+ otherwise. */
+
+static int
+attach_proc_task_lwp_callback (ptid_t ptid)
+{
+ /* Is this a new thread? */
+ if (find_thread_ptid (ptid) == NULL)
+ {
+ int lwpid = ptid_get_lwp (ptid);
+ int err;
+
+ if (debug_threads)
+ debug_printf ("Found new lwp %d\n", lwpid);
+
+ err = linux_attach_lwp (ptid);
+
+ /* Be quiet if we simply raced with the thread exiting. EPERM
+ is returned if the thread's task still exists, and is marked
+ as exited or zombie, as well as other conditions, so in that
+ case, confirm the status in /proc/PID/status. */
+ if (err == ESRCH
+ || (err == EPERM && linux_proc_pid_is_gone (lwpid)))
+ {
+ if (debug_threads)
+ {
+ debug_printf ("Cannot attach to lwp %d: "
+ "thread is gone (%d: %s)\n",
+ lwpid, err, strerror (err));
+ }
+ }
+ else if (err != 0)
+ {
+ warning (_("Cannot attach to lwp %d: %s"),
+ lwpid,
+ linux_ptrace_attach_fail_reason_string (ptid, err));
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+static void async_file_mark (void);
+
/* Attach to PID. If PID is the tgid, attach to it and all
of its threads. */
static int
linux_attach (unsigned long pid)
{
+ struct process_info *proc;
+ struct thread_info *initial_thread;
ptid_t ptid = ptid_build (pid, pid, 0);
int err;
err = linux_attach_lwp (ptid);
if (err != 0)
error ("Cannot attach to process %ld: %s",
- pid, linux_attach_fail_reason_string (ptid, err));
-
- linux_add_process (pid, 1);
-
- if (!non_stop)
- {
- struct thread_info *thread;
-
- /* Don't ignore the initial SIGSTOP if we just attached to this
- process. It will be collected by wait shortly. */
- thread = find_thread_ptid (ptid_build (pid, pid, 0));
- thread->last_resume_kind = resume_stop;
- }
-
- if (linux_proc_get_tgid (pid) == pid)
+ pid, linux_ptrace_attach_fail_reason_string (ptid, err));
+
+ proc = linux_add_process (pid, 1);
+
+ /* Don't ignore the initial SIGSTOP if we just attached to this
+ process. It will be collected by wait shortly. */
+ initial_thread = find_thread_ptid (ptid_build (pid, pid, 0));
+ initial_thread->last_resume_kind = resume_stop;
+
+ /* We must attach to every LWP. If /proc is mounted, use that to
+ find them now. On the one hand, the inferior may be using raw
+ clone instead of using pthreads. On the other hand, even if it
+ is using pthreads, GDB may not be connected yet (thread_db needs
+ to do symbol lookups, through qSymbol). Also, thread_db walks
+ structures in the inferior's address space to find the list of
+ threads/LWPs, and those structures may well be corrupted. Note
+ that once thread_db is loaded, we'll still use it to list threads
+ and associate pthread info with each LWP. */
+ linux_proc_attach_tgid_threads (pid, attach_proc_task_lwp_callback);
+
+ /* GDB will shortly read the xml target description for this
+ process, to figure out the process' architecture. But the target
+ description is only filled in when the first process/thread in
+ the thread group reports its initial PTRACE_ATTACH SIGSTOP. Do
+ that now, otherwise, if GDB is fast enough, it could read the
+ target description _before_ that initial stop. */
+ if (non_stop)
{
- DIR *dir;
- char pathname[128];
+ struct lwp_info *lwp;
+ int wstat, lwpid;
+ ptid_t pid_ptid = pid_to_ptid (pid);
- sprintf (pathname, "/proc/%ld/task", pid);
+ lwpid = linux_wait_for_event_filtered (pid_ptid, pid_ptid,
+ &wstat, __WALL);
+ gdb_assert (lwpid > 0);
- dir = opendir (pathname);
+ lwp = find_lwp_pid (pid_to_ptid (lwpid));
- if (!dir)
+ if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGSTOP)
{
- fprintf (stderr, "Could not open /proc/%ld/task.\n", pid);
- fflush (stderr);
+ lwp->status_pending_p = 1;
+ lwp->status_pending = wstat;
}
- else
- {
- /* At this point we attached to the tgid. Scan the task for
- existing threads. */
- int new_threads_found;
- int iterations = 0;
-
- while (iterations < 2)
- {
- struct dirent *dp;
-
- new_threads_found = 0;
- /* Add all the other threads. While we go through the
- threads, new threads may be spawned. Cycle through
- the list of threads until we have done two iterations without
- finding new threads. */
- while ((dp = readdir (dir)) != NULL)
- {
- unsigned long lwp;
- ptid_t ptid;
-
- /* Fetch one lwp. */
- lwp = strtoul (dp->d_name, NULL, 10);
-
- ptid = ptid_build (pid, lwp, 0);
-
- /* Is this a new thread? */
- if (lwp != 0 && find_thread_ptid (ptid) == NULL)
- {
- int err;
- if (debug_threads)
- debug_printf ("Found new lwp %ld\n", lwp);
+ initial_thread->last_resume_kind = resume_continue;
- err = linux_attach_lwp (ptid);
- if (err != 0)
- warning ("Cannot attach to lwp %ld: %s",
- lwp,
- linux_attach_fail_reason_string (ptid, err));
+ async_file_mark ();
- new_threads_found++;
- }
- }
-
- if (!new_threads_found)
- iterations++;
- else
- iterations = 0;
-
- rewinddir (dir);
- }
- closedir (dir);
- }
+ gdb_assert (proc->tdesc != NULL);
}
return 0;
static int
second_thread_of_pid_p (struct inferior_list_entry *entry, void *args)
{
- struct counter *counter = args;
+ struct counter *counter = (struct counter *) args;
if (ptid_get_pid (entry->id) == counter->pid)
{
res = my_waitpid (lwpid, &wstat, __WCLONE);
} while (res > 0 && WIFSTOPPED (wstat));
- gdb_assert (res > 0);
+ /* Even if it was stopped, the child may have already disappeared.
+ E.g., if it was killed by SIGKILL. */
+ if (res < 0 && errno != ECHILD)
+ perror_with_name ("kill_wait_lwp");
}
/* Callback for `find_inferior'. Kills an lwp of a given process,
if (process == NULL)
return -1;
+ /* As there's a step over already in progress, let it finish first,
+ otherwise nesting a stabilize_threads operation on top gets real
+ messy. */
+ complete_ongoing_step_over ();
+
/* Stop all threads before detaching. First, ptrace requires that
the thread is stopped to sucessfully detach. Second, thread_db
may need to uninstall thread event breakpoints from memory, which
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
- struct process_info *process = proc;
+ struct process_info *process = (struct process_info *) proc;
if (pid_of (thread) == pid_of (process))
delete_lwp (lwp);
find_inferior (&all_threads, delete_lwp_callback, process);
/* Freeing all private data. */
- priv = process->private;
+ priv = process->priv;
free (priv->arch_private);
free (priv);
- process->private = NULL;
+ process->priv = NULL;
remove_process (process);
}
exited but we still haven't been able to report it to GDB, we'll
hold on to the last lwp of the dead process. */
if (lwp != NULL)
- return !lwp->dead;
+ return !lwp_is_marked_dead (lwp);
else
return 0;
}
+/* Return 1 if this lwp still has an interesting status pending. If
+ not (e.g., it had stopped for a breakpoint that is gone), return
+ false. */
+
+static int
+thread_still_has_status_pending_p (struct thread_info *thread)
+{
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ if (!lp->status_pending_p)
+ return 0;
+
+ if (thread->last_resume_kind != resume_stop
+ && (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT))
+ {
+ struct thread_info *saved_thread;
+ CORE_ADDR pc;
+ int discard = 0;
+
+ gdb_assert (lp->last_status != 0);
+
+ pc = get_pc (lp);
+
+ saved_thread = current_thread;
+ current_thread = thread;
+
+ if (pc != lp->stop_pc)
+ {
+ if (debug_threads)
+ debug_printf ("PC of %ld changed\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+
+#if !USE_SIGTRAP_SIGINFO
+ else if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && !(*the_low_target.breakpoint_at) (pc))
+ {
+ if (debug_threads)
+ debug_printf ("previous SW breakpoint of %ld gone\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+ else if (lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT
+ && !hardware_breakpoint_inserted_here (pc))
+ {
+ if (debug_threads)
+ debug_printf ("previous HW breakpoint of %ld gone\n",
+ lwpid_of (thread));
+ discard = 1;
+ }
+#endif
+
+ current_thread = saved_thread;
+
+ if (discard)
+ {
+ if (debug_threads)
+ debug_printf ("discarding pending breakpoint status\n");
+ lp->status_pending_p = 0;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Returns true if LWP is resumed from the client's perspective. */
+
+static int
+lwp_resumed (struct lwp_info *lwp)
+{
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ if (thread->last_resume_kind != resume_stop)
+ return 1;
+
+ /* Did gdb send us a `vCont;t', but we haven't reported the
+ corresponding stop to gdb yet? If so, the thread is still
+ resumed/running from gdb's perspective. */
+ if (thread->last_resume_kind == resume_stop
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ return 1;
+
+ return 0;
+}
+
/* Return 1 if this lwp has an interesting status pending. */
static int
status_pending_p_callback (struct inferior_list_entry *entry, void *arg)
{
struct thread_info *thread = (struct thread_info *) entry;
- struct lwp_info *lwp = get_thread_lwp (thread);
+ struct lwp_info *lp = get_thread_lwp (thread);
ptid_t ptid = * (ptid_t *) arg;
/* Check if we're only interested in events from a specific process
- or its lwps. */
- if (!ptid_equal (minus_one_ptid, ptid)
- && ptid_get_pid (ptid) != ptid_get_pid (thread->entry.id))
+ or a specific LWP. */
+ if (!ptid_match (ptid_of (thread), ptid))
return 0;
- /* If we got a `vCont;t', but we haven't reported a stop yet, do
- report any status pending the LWP may have. */
- if (thread->last_resume_kind == resume_stop
- && thread->last_status.kind != TARGET_WAITKIND_IGNORE)
+ if (!lwp_resumed (lp))
return 0;
- return lwp->status_pending_p;
+ if (lp->status_pending_p
+ && !thread_still_has_status_pending_p (thread))
+ {
+ linux_resume_one_lwp (lp, lp->stepping, GDB_SIGNAL_0, NULL);
+ return 0;
+ }
+
+ return lp->status_pending_p;
}
static int
return count;
}
+/* The arguments passed to iterate_over_lwps. */
+
+struct iterate_over_lwps_args
+{
+ /* The FILTER argument passed to iterate_over_lwps. */
+ ptid_t filter;
+
+ /* The CALLBACK argument passed to iterate_over_lwps. */
+ iterate_over_lwps_ftype *callback;
+
+ /* The DATA argument passed to iterate_over_lwps. */
+ void *data;
+};
+
+/* Callback for find_inferior used by iterate_over_lwps to filter
+ calls to the callback supplied to that function. Returning a
+ nonzero value causes find_inferiors to stop iterating and return
+ the current inferior_list_entry. Returning zero indicates that
+ find_inferiors should continue iterating. */
+
+static int
+iterate_over_lwps_filter (struct inferior_list_entry *entry, void *args_p)
+{
+ struct iterate_over_lwps_args *args
+ = (struct iterate_over_lwps_args *) args_p;
+
+ if (ptid_match (entry->id, args->filter))
+ {
+ struct thread_info *thr = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thr);
+
+ return (*args->callback) (lwp, args->data);
+ }
+
+ return 0;
+}
+
+/* See nat/linux-nat.h. */
+
+struct lwp_info *
+iterate_over_lwps (ptid_t filter,
+ iterate_over_lwps_ftype callback,
+ void *data)
+{
+ struct iterate_over_lwps_args args = {filter, callback, data};
+ struct inferior_list_entry *entry;
+
+ entry = find_inferior (&all_threads, iterate_over_lwps_filter, &args);
+ if (entry == NULL)
+ return NULL;
+
+ return get_thread_lwp ((struct thread_info *) entry);
+}
+
/* Detect zombie thread group leaders, and "exit" them. We can't reap
their exits until all other threads in the group have exited. */
leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
linux_proc_pid_is_zombie (leader_pid));
- if (leader_lp != NULL
+ if (leader_lp != NULL && !leader_lp->stopped
/* Check if there are other threads in the group, as we may
have raced with the inferior simply exiting. */
&& !last_thread_of_process_p (leader_pid)
return 0;
}
+/* Increment LWP's suspend count. */
+
+static void
+lwp_suspended_inc (struct lwp_info *lwp)
+{
+ lwp->suspended++;
+
+ if (debug_threads && lwp->suspended > 4)
+ {
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ debug_printf ("LWP %ld has a suspiciously high suspend count,"
+ " suspended=%d\n", lwpid_of (thread), lwp->suspended);
+ }
+}
+
+/* Decrement LWP's suspend count. */
+
+static void
+lwp_suspended_decr (struct lwp_info *lwp)
+{
+ lwp->suspended--;
+
+ if (lwp->suspended < 0)
+ {
+ struct thread_info *thread = get_lwp_thread (lwp);
+
+ internal_error (__FILE__, __LINE__,
+ "unsuspend LWP %ld, suspended=%d\n", lwpid_of (thread),
+ lwp->suspended);
+ }
+}
+
/* This function should only be called if the LWP got a SIGTRAP.
Handle any tracepoint steps or hits. Return true if a tracepoint
struct thread_info *tinfo = get_lwp_thread (lwp);
int tpoint_related_event = 0;
+ gdb_assert (lwp->suspended == 0);
+
/* If this tracepoint hit causes a tracing stop, we'll immediately
uninsert tracepoints. To do this, we temporarily pause all
threads, unpatch away, and then unpause threads. We need to make
sure the unpausing doesn't resume LWP too. */
- lwp->suspended++;
+ lwp_suspended_inc (lwp);
/* And we need to be sure that any all-threads-stopping doesn't try
to move threads out of the jump pads, as it could deadlock the
actions. */
tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
- lwp->suspended--;
+ lwp_suspended_decr (lwp);
gdb_assert (lwp->suspended == 0);
gdb_assert (!stabilizing_threads || lwp->collecting_fast_tracepoint);
"stopping all threads momentarily.\n");
stop_all_lwps (1, lwp);
- cancel_breakpoints ();
delete_breakpoint (lwp->exit_jump_pad_bkpt);
lwp->exit_jump_pad_bkpt = NULL;
}
}
- p_sig = xmalloc (sizeof (*p_sig));
+ p_sig = XCNEW (struct pending_signals);
p_sig->prev = lwp->pending_signals_to_report;
p_sig->signal = WSTOPSIG (*wstat);
- memset (&p_sig->info, 0, sizeof (siginfo_t));
+
ptrace (PTRACE_GETSIGINFO, lwpid_of (thread), (PTRACE_TYPE_ARG3) 0,
&p_sig->info);
return 0;
}
-/* Arrange for a breakpoint to be hit again later. We don't keep the
- SIGTRAP status and don't forward the SIGTRAP signal to the LWP. We
- will handle the current event, eventually we will resume this LWP,
- and this breakpoint will trap again. */
+/* Fetch the possibly triggered data watchpoint info and store it in
+ CHILD.
+
+ On some archs, like x86, that use debug registers to set
+ watchpoints, it's possible that the way to know which watched
+ address trapped, is to check the register that is used to select
+ which address to watch. Problem is, between setting the watchpoint
+ and reading back which data address trapped, the user may change
+ the set of watchpoints, and, as a consequence, GDB changes the
+ debug registers in the inferior. To avoid reading back a stale
+ stopped-data-address when that happens, we cache in LP the fact
+ that a watchpoint trapped, and the corresponding data address, as
+ soon as we see CHILD stop with a SIGTRAP. If GDB changes the debug
+ registers meanwhile, we have the cached data we can rely on. */
static int
-cancel_breakpoint (struct lwp_info *lwp)
+check_stopped_by_watchpoint (struct lwp_info *child)
{
- struct thread_info *saved_thread;
-
- /* There's nothing to do if we don't support breakpoints. */
- if (!supports_breakpoints ())
- return 0;
-
- /* breakpoint_at reads from current inferior. */
- saved_thread = current_thread;
- current_thread = get_lwp_thread (lwp);
-
- if ((*the_low_target.breakpoint_at) (lwp->stop_pc))
+ if (the_low_target.stopped_by_watchpoint != NULL)
{
- if (debug_threads)
- debug_printf ("CB: Push back breakpoint for %s\n",
- target_pid_to_str (ptid_of (current_thread)));
+ struct thread_info *saved_thread;
- /* Back up the PC if necessary. */
- if (the_low_target.decr_pc_after_break)
+ saved_thread = current_thread;
+ current_thread = get_lwp_thread (child);
+
+ if (the_low_target.stopped_by_watchpoint ())
{
- struct regcache *regcache
- = get_thread_regcache (current_thread, 1);
- (*the_low_target.set_pc) (regcache, lwp->stop_pc);
- }
+ child->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
- current_thread = saved_thread;
- return 1;
- }
- else
- {
- if (debug_threads)
- debug_printf ("CB: No breakpoint found at %s for [%s]\n",
- paddress (lwp->stop_pc),
- target_pid_to_str (ptid_of (current_thread)));
+ if (the_low_target.stopped_data_address != NULL)
+ child->stopped_data_address
+ = the_low_target.stopped_data_address ();
+ else
+ child->stopped_data_address = 0;
+ }
+
+ current_thread = saved_thread;
}
- current_thread = saved_thread;
- return 0;
+ return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
}
-/* Return true if the event in LP may be caused by breakpoint. */
+/* Return the ptrace options that we want to try to enable. */
static int
-lp_status_maybe_breakpoint (struct lwp_info *lp)
+linux_low_ptrace_options (int attached)
{
- return (lp->status_pending_p
- && WIFSTOPPED (lp->status_pending)
- && (WSTOPSIG (lp->status_pending) == SIGTRAP
- /* SIGILL and SIGSEGV are also treated as traps in case a
- breakpoint is inserted at the current PC. */
- || WSTOPSIG (lp->status_pending) == SIGILL
- || WSTOPSIG (lp->status_pending) == SIGSEGV));
+ int options = 0;
+
+ if (!attached)
+ options |= PTRACE_O_EXITKILL;
+
+ if (report_fork_events)
+ options |= PTRACE_O_TRACEFORK;
+
+ if (report_vfork_events)
+ options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
+
+ if (report_exec_events)
+ options |= PTRACE_O_TRACEEXEC;
+
+ return options;
}
/* Do low-level handling of the event, and check if we should go on
NULL otherwise. */
static struct lwp_info *
-linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
+linux_low_filter_event (int lwpid, int wstat)
{
struct lwp_info *child;
struct thread_info *thread;
+ int have_stop_pc = 0;
child = find_lwp_pid (pid_to_ptid (lwpid));
+ /* Check for stop events reported by a process we didn't already
+ know about - anything not already in our LWP list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is.
+
+ But note the case of a non-leader thread exec'ing after the
+ leader having exited, and gone from our lists (because
+ check_zombie_leaders deleted it). The non-leader thread
+ changes its tid to the tgid. */
+
+ if (WIFSTOPPED (wstat) && child == NULL && WSTOPSIG (wstat) == SIGTRAP
+ && linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC)
+ {
+ ptid_t child_ptid;
+
+ /* A multi-thread exec after we had seen the leader exiting. */
+ if (debug_threads)
+ {
+ debug_printf ("LLW: Re-adding thread group leader LWP %d"
+ "after exec.\n", lwpid);
+ }
+
+ child_ptid = ptid_build (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_thread = child->thread;
+ }
+
/* If we didn't find a process, one of two things presumably happened:
- A process we started and then detached from has exited. Ignore it.
- A process we are controlling has forked and the new child's stop
child->last_status = wstat;
- if (WIFSTOPPED (wstat))
+ /* Check if the thread has exited. */
+ if ((WIFEXITED (wstat) || WIFSIGNALED (wstat)))
{
- struct process_info *proc;
-
- /* Architecture-specific setup after inferior is running. This
- needs to happen after we have attached to the inferior and it
- is stopped for the first time, but before we access any
- inferior registers. */
- proc = find_process_pid (pid_of (thread));
- if (proc->private->new_inferior)
+ if (debug_threads)
+ debug_printf ("LLFE: %d exited.\n", lwpid);
+ /* If there is at least one more LWP, then the exit signal was
+ not the end of the debugged application and should be
+ ignored, unless GDB wants to hear about thread exits. */
+ if (report_thread_events
+ || last_thread_of_process_p (pid_of (thread)))
{
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = thread;
-
- the_low_target.arch_setup ();
-
- current_thread = saved_thread;
-
- proc->private->new_inferior = 0;
+ /* Since events are serialized to GDB core, and we can't
+ report this one right now. Leave the status pending for
+ the next time we're able to report it. */
+ mark_lwp_dead (child, wstat);
+ return child;
}
- }
-
- /* Store the STOP_PC, with adjustment applied. This depends on the
- architecture being defined already (so that CHILD has a valid
- regcache), and on LAST_STATUS being set (to check for SIGTRAP or
- not). */
- if (WIFSTOPPED (wstat))
- {
- if (debug_threads
- && the_low_target.get_pc != NULL)
+ else
{
- struct thread_info *saved_thread;
- struct regcache *regcache;
- CORE_ADDR pc;
-
- saved_thread = current_thread;
- current_thread = thread;
- regcache = get_thread_regcache (current_thread, 1);
- pc = (*the_low_target.get_pc) (regcache);
- debug_printf ("linux_low_filter_event: pc is 0x%lx\n", (long) pc);
- current_thread = saved_thread;
+ delete_lwp (child);
+ return NULL;
}
-
- child->stop_pc = get_stop_pc (child);
}
- /* Fetch the possibly triggered data watchpoint info and store it in
- CHILD.
-
- On some archs, like x86, that use debug registers to set
- watchpoints, it's possible that the way to know which watched
- address trapped, is to check the register that is used to select
- which address to watch. Problem is, between setting the
- watchpoint and reading back which data address trapped, the user
- may change the set of watchpoints, and, as a consequence, GDB
- changes the debug registers in the inferior. To avoid reading
- back a stale stopped-data-address when that happens, we cache in
- LP the fact that a watchpoint trapped, and the corresponding data
- address, as soon as we see CHILD stop with a SIGTRAP. If GDB
- changes the debug registers meanwhile, we have the cached data we
- can rely on. */
+ gdb_assert (WIFSTOPPED (wstat));
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP)
+ if (WIFSTOPPED (wstat))
{
- if (the_low_target.stopped_by_watchpoint == NULL)
- {
- child->stopped_by_watchpoint = 0;
- }
- else
- {
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = thread;
-
- child->stopped_by_watchpoint
- = the_low_target.stopped_by_watchpoint ();
+ struct process_info *proc;
- if (child->stopped_by_watchpoint)
+ /* Architecture-specific setup after inferior is running. */
+ proc = find_process_pid (pid_of (thread));
+ if (proc->tdesc == NULL)
+ {
+ if (proc->attached)
{
- if (the_low_target.stopped_data_address != NULL)
- child->stopped_data_address
- = the_low_target.stopped_data_address ();
- else
- child->stopped_data_address = 0;
+ /* This needs to happen after we have attached to the
+ inferior and it is stopped for the first time, but
+ before we access any inferior registers. */
+ linux_arch_setup_thread (thread);
+ }
+ else
+ {
+ /* The process is started, but GDBserver will do
+ architecture-specific setup after the program stops at
+ the first instruction. */
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
}
-
- current_thread = saved_thread;
}
}
if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
{
struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
- linux_enable_event_reporting (lwpid, proc->attached);
+ linux_enable_event_reporting (lwpid, options);
child->must_set_ptrace_flags = 0;
}
+ /* Be careful to not overwrite stop_pc until
+ check_stopped_by_breakpoint is called. */
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
&& linux_is_extended_waitstatus (wstat))
{
- handle_extended_wait (child, wstat);
- return NULL;
+ child->stop_pc = get_pc (child);
+ if (handle_extended_wait (&child, wstat))
+ {
+ /* The event has been handled, so just return without
+ reporting it. */
+ return NULL;
+ }
+ }
+
+ /* Check first whether this was a SW/HW breakpoint before checking
+ watchpoints, because at least s390 can't tell the data address of
+ hardware watchpoint hits, and returns stopped-by-watchpoint as
+ long as there's a watchpoint set. */
+ if (WIFSTOPPED (wstat) && linux_wstatus_maybe_breakpoint (wstat))
+ {
+ if (check_stopped_by_breakpoint (child))
+ have_stop_pc = 1;
}
+ /* Note that TRAP_HWBKPT can indicate either a hardware breakpoint
+ or hardware watchpoint. Check which is which if we got
+ TARGET_STOPPED_BY_HW_BREAKPOINT. Likewise, we may have single
+ stepped an instruction that triggered a watchpoint. In that
+ case, on some architectures (such as x86), instead of
+ TRAP_HWBKPT, si_code indicates TRAP_TRACE, and we need to check
+ the debug registers separately. */
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+ && child->stop_reason != TARGET_STOPPED_BY_SW_BREAKPOINT)
+ check_stopped_by_watchpoint (child);
+
+ if (!have_stop_pc)
+ child->stop_pc = get_pc (child);
+
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
&& child->stop_expected)
{
{
/* We want to report the stop to the core. Treat the
SIGSTOP as a normal event. */
+ if (debug_threads)
+ debug_printf ("LLW: resume_stop SIGSTOP caught for %s.\n",
+ target_pid_to_str (ptid_of (thread)));
}
else if (stopping_threads != NOT_STOPPING_THREADS)
{
/* Stopping threads. We don't want this SIGSTOP to end up
- pending in the FILTER_PTID handling below. */
+ pending. */
+ if (debug_threads)
+ debug_printf ("LLW: SIGSTOP caught for %s "
+ "while stopping threads.\n",
+ target_pid_to_str (ptid_of (thread)));
return NULL;
}
else
{
- /* Filter out the event. */
+ /* This is a delayed SIGSTOP. Filter out the event. */
+ if (debug_threads)
+ debug_printf ("LLW: %s %s, 0, 0 (discard delayed SIGSTOP)\n",
+ child->stepping ? "step" : "continue",
+ target_pid_to_str (ptid_of (thread)));
+
linux_resume_one_lwp (child, child->stepping, 0, NULL);
return NULL;
}
}
- /* Check if the thread has exited. */
- if ((WIFEXITED (wstat) || WIFSIGNALED (wstat))
- && num_lwps (pid_of (thread)) > 1)
- {
- if (debug_threads)
- debug_printf ("LLW: %d exited.\n", lwpid);
-
- /* If there is at least one more LWP, then the exit signal
- was not the end of the debugged application and should be
- ignored. */
- delete_lwp (child);
- return NULL;
- }
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
+}
- if (!ptid_match (ptid_of (thread), filter_ptid))
- {
- if (debug_threads)
- debug_printf ("LWP %d got an event %06x, leaving pending.\n",
- lwpid, wstat);
+/* Resume LWPs that are currently stopped without any pending status
+ to report, but are resumed from the core's perspective. */
- if (WIFSTOPPED (wstat))
- {
- child->status_pending_p = 1;
- child->status_pending = wstat;
+static void
+resume_stopped_resumed_lwps (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lp = get_thread_lwp (thread);
- if (WSTOPSIG (wstat) != SIGSTOP)
- {
- /* Cancel breakpoint hits. The breakpoint may be
- removed before we fetch events from this process to
- report to the core. It is best not to assume the
- moribund breakpoints heuristic always handles these
- cases --- it could be too many events go through to
- the core before this one is handled. All-stop always
- cancels breakpoint hits in all threads. */
- if (non_stop
- && lp_status_maybe_breakpoint (child)
- && cancel_breakpoint (child))
- {
- /* Throw away the SIGTRAP. */
- child->status_pending_p = 0;
+ if (lp->stopped
+ && !lp->suspended
+ && !lp->status_pending_p
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+ {
+ int step = thread->last_resume_kind == resume_step;
- if (debug_threads)
- debug_printf ("LLW: LWP %d hit a breakpoint while"
- " waiting for another process;"
- " cancelled it\n", lwpid);
- }
- }
- }
- else if (WIFEXITED (wstat) || WIFSIGNALED (wstat))
- {
- if (debug_threads)
- debug_printf ("LLWE: process %d exited while fetching "
- "event from another LWP\n", lwpid);
-
- /* This was the last lwp in the process. Since events are
- serialized to GDB core, and we can't report this one
- right now, but GDB core and the other target layers will
- want to be notified about the exit code/signal, leave the
- status pending for the next time we're able to report
- it. */
- mark_lwp_dead (child, wstat);
- }
+ if (debug_threads)
+ debug_printf ("RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
+ target_pid_to_str (ptid_of (thread)),
+ paddress (lp->stop_pc),
+ step);
- return NULL;
+ linux_resume_one_lwp (lp, step, GDB_SIGNAL_0, NULL);
}
-
- return child;
}
-/* When the event-loop is doing a step-over, this points at the thread
- being stepped. */
-ptid_t step_over_bkpt;
-
/* Wait for an event from child(ren) WAIT_PTID, and return any that
match FILTER_PTID (leaving others pending). The PTIDs can be:
minus_one_ptid, to specify any child; a pid PTID, specifying all
sigfillset (&block_mask);
sigprocmask (SIG_BLOCK, &block_mask, &prev_mask);
+ /* Always pull all events out of the kernel. We'll randomly select
+ an event LWP out of all that have events, to prevent
+ starvation. */
while (event_child == NULL)
{
pid_t ret = 0;
- When a non-leader thread execs, that thread just vanishes
without reporting an exit (so we'd hang if we waited for it
explicitly in that case). The exec event is reported to
- the TGID pid (although we don't currently enable exec
- events). */
+ the TGID pid. */
errno = 0;
ret = my_waitpid (-1, wstatp, options | WNOHANG);
(long) ret, status_to_str (*wstatp));
}
- event_child = linux_low_filter_event (filter_ptid,
- ret, *wstatp);
- if (event_child != NULL)
- {
- /* We got an event to report to the core. */
- event_thread = get_lwp_thread (event_child);
- break;
- }
-
+ /* Filter all events. IOW, leave all events pending. We'll
+ randomly select an event LWP out of all that have events
+ below. */
+ linux_low_filter_event (ret, *wstatp);
/* Retry until nothing comes out of waitpid. A single
SIGCHLD can indicate more than one child stopped. */
continue;
}
+ /* Now that we've pulled all events out of the kernel, resume
+ LWPs that don't have an interesting event to report. */
+ if (stopping_threads == NOT_STOPPING_THREADS)
+ for_each_inferior (&all_threads, resume_stopped_resumed_lwps);
+
+ /* ... 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);
+ if (event_thread != NULL)
+ {
+ event_child = get_thread_lwp (event_thread);
+ *wstatp = event_child->status_pending;
+ event_child->status_pending_p = 0;
+ event_child->status_pending = 0;
+ break;
+ }
+
/* Check for zombie thread group leaders. Those can't be reaped
until all other threads in the thread group are. */
check_zombie_leaders ();
current_thread = event_thread;
- /* Check for thread exit. */
- if (! WIFSTOPPED (*wstatp))
- {
- gdb_assert (last_thread_of_process_p (pid_of (event_thread)));
-
- if (debug_threads)
- debug_printf ("LWP %d is the last lwp of process. "
- "Process %ld exiting.\n",
- pid_of (event_thread), lwpid_of (event_thread));
- return lwpid_of (event_thread);
- }
-
return lwpid_of (event_thread);
}
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lp = get_thread_lwp (thread);
- int *count = data;
+ int *count = (int *) data;
gdb_assert (count != NULL);
- /* Count only resumed LWPs that have a SIGTRAP event pending that
- should be reported to GDB. */
+ /* Count only resumed LWPs that have an event pending. */
if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
- && thread->last_resume_kind != resume_stop
- && lp_status_maybe_breakpoint (lp)
- && !breakpoint_inserted_here (lp->stop_pc))
+ && lp->status_pending_p)
(*count)++;
return 0;
return 0;
}
-/* Select the Nth LWP that has had a SIGTRAP event that should be
- reported to GDB. */
+/* Select the Nth LWP that has had an event. */
static int
select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lp = get_thread_lwp (thread);
- int *selector = data;
+ int *selector = (int *) data;
gdb_assert (selector != NULL);
- /* Select only resumed LWPs that have a SIGTRAP event pending. */
- if (thread->last_resume_kind != resume_stop
- && thread->last_status.kind == TARGET_WAITKIND_IGNORE
- && lp_status_maybe_breakpoint (lp)
- && !breakpoint_inserted_here (lp->stop_pc))
+ /* Select only resumed LWPs that have an event pending. */
+ if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+ && lp->status_pending_p)
if ((*selector)-- == 0)
return 1;
return 0;
}
-static int
-cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data)
-{
- struct thread_info *thread = (struct thread_info *) entry;
- struct lwp_info *lp = get_thread_lwp (thread);
- struct lwp_info *event_lp = data;
-
- /* Leave the LWP that has been elected to receive a SIGTRAP alone. */
- if (lp == event_lp)
- return 0;
-
- /* If a LWP other than the LWP that we're reporting an event for has
- hit a GDB breakpoint (as opposed to some random trap signal),
- then just arrange for it to hit it again later. We don't keep
- the SIGTRAP status and don't forward the SIGTRAP signal to the
- LWP. We will handle the current event, eventually we will resume
- all LWPs, and this one will get its breakpoint trap again.
-
- If we do not do this, then we run the risk that the user will
- delete or disable the breakpoint, but the LWP will have already
- tripped on it. */
-
- if (thread->last_resume_kind != resume_stop
- && thread->last_status.kind == TARGET_WAITKIND_IGNORE
- && lp_status_maybe_breakpoint (lp)
- && !lp->stepping
- && !lp->stopped_by_watchpoint
- && cancel_breakpoint (lp))
- /* Throw away the SIGTRAP. */
- lp->status_pending_p = 0;
-
- return 0;
-}
-
-static void
-linux_cancel_breakpoints (void)
-{
- find_inferior (&all_threads, cancel_breakpoints_callback, NULL);
-}
-
/* Select one LWP out of those that have events pending. */
static void
{
int num_events = 0;
int random_selector;
- struct thread_info *event_thread;
-
- /* Give preference to any LWP that is being single-stepped. */
- event_thread
- = (struct thread_info *) find_inferior (&all_threads,
- select_singlestep_lwp_callback,
- NULL);
- if (event_thread != NULL)
+ struct thread_info *event_thread = NULL;
+
+ /* In all-stop, give preference to the LWP that is being
+ single-stepped. There will be at most one, and it's the LWP that
+ the core is most interested in. If we didn't do this, then we'd
+ have to handle pending step SIGTRAPs somehow in case the core
+ later continues the previously-stepped thread, otherwise we'd
+ report the pending SIGTRAP, and the core, not having stepped the
+ thread, wouldn't understand what the trap was for, and therefore
+ would report it to the user as a random signal. */
+ if (!non_stop)
{
- if (debug_threads)
- debug_printf ("SEL: Select single-step %s\n",
- target_pid_to_str (ptid_of (event_thread)));
+ event_thread
+ = (struct thread_info *) find_inferior (&all_threads,
+ select_singlestep_lwp_callback,
+ NULL);
+ if (event_thread != NULL)
+ {
+ if (debug_threads)
+ debug_printf ("SEL: Select single-step %s\n",
+ target_pid_to_str (ptid_of (event_thread)));
+ }
}
- else
+ if (event_thread == NULL)
{
/* No single-stepping LWP. Select one at random, out of those
- which have had SIGTRAP events. */
+ which have had events. */
- /* First see how many SIGTRAP events we have. */
+ /* First see how many events we have. */
find_inferior (&all_threads, count_events_callback, &num_events);
+ gdb_assert (num_events > 0);
- /* Now randomly pick a LWP out of those that have had a SIGTRAP. */
+ /* Now randomly pick a LWP out of those that have had
+ events. */
random_selector = (int)
((num_events * (double) rand ()) / (RAND_MAX + 1.0));
if (lwp == except)
return 0;
- lwp->suspended--;
-
- gdb_assert (lwp->suspended >= 0);
+ lwp_suspended_decr (lwp);
return 0;
}
lwp = get_thread_lwp (current_thread);
/* Lock it. */
- lwp->suspended++;
+ lwp_suspended_inc (lwp);
if (ourstatus.value.sig != GDB_SIGNAL_0
|| current_thread->last_resume_kind == resume_stop)
}
}
+/* Convenience function that is called when the kernel reports an
+ event that is not passed out to GDB. */
+
+static ptid_t
+ignore_event (struct target_waitstatus *ourstatus)
+{
+ /* If we got an event, there may still be others, as a single
+ SIGCHLD can indicate more than one child stopped. This forces
+ another target_wait call. */
+ async_file_mark ();
+
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ return null_ptid;
+}
+
+/* Convenience function that is called when the kernel reports an exit
+ event. This decides whether to report the event to GDB as a
+ process exit event, a thread exit event, or to suppress the
+ event. */
+
+static ptid_t
+filter_exit_event (struct lwp_info *event_child,
+ struct target_waitstatus *ourstatus)
+{
+ struct thread_info *thread = get_lwp_thread (event_child);
+ ptid_t ptid = ptid_of (thread);
+
+ if (!last_thread_of_process_p (pid_of (thread)))
+ {
+ if (report_thread_events)
+ ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED;
+ else
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+ delete_lwp (event_child);
+ }
+ return ptid;
+}
+
/* Wait for process, returns status. */
static ptid_t
if (target_options & TARGET_WNOHANG)
options |= WNOHANG;
-retry:
bp_explains_trap = 0;
trace_event = 0;
in_step_range = 0;
}
}
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ return filter_exit_event (event_child, ourstatus);
+
return ptid_of (current_thread);
}
+ /* If step-over executes a breakpoint instruction, it means a
+ gdb/gdbserver breakpoint had been planted on top of a permanent
+ breakpoint. The PC has been adjusted by
+ check_stopped_by_breakpoint to point at the breakpoint address.
+ Advance the PC manually past the breakpoint, otherwise the
+ program would keep trapping the permanent breakpoint forever. */
+ if (!ptid_equal (step_over_bkpt, null_ptid)
+ && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ {
+ int increment_pc = 0;
+ int breakpoint_kind = 0;
+ CORE_ADDR stop_pc = event_child->stop_pc;
+
+ breakpoint_kind = the_target->breakpoint_kind_from_pc (&stop_pc);
+ the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc);
+
+ if (debug_threads)
+ {
+ debug_printf ("step-over for %s executed software breakpoint\n",
+ target_pid_to_str (ptid_of (current_thread)));
+ }
+
+ if (increment_pc != 0)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+
+ event_child->stop_pc += increment_pc;
+ (*the_low_target.set_pc) (regcache, event_child->stop_pc);
+
+ if (!(*the_low_target.breakpoint_at) (event_child->stop_pc))
+ event_child->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ }
+ }
+
/* If this event was not handled before, and is not a SIGTRAP, we
report it. SIGILL and SIGSEGV are also treated as traps in case
a breakpoint is inserted at the current PC. If this target does
WSTOPSIG (w), lwpid_of (current_thread));
linux_resume_one_lwp (event_child, 0, 0, NULL);
- goto retry;
+
+ return ignore_event (ourstatus);
}
}
care of while the breakpoint is still
inserted. */
stop_all_lwps (1, event_child);
- cancel_breakpoints ();
delete_breakpoint (event_child->exit_jump_pad_bkpt);
event_child->exit_jump_pad_bkpt = NULL;
any that GDB specifically requested we ignore. But never ignore
SIGSTOP if we sent it ourselves, and do not ignore signals when
stepping - they may require special handling to skip the signal
- handler. */
+ handler. Also never ignore signals that could be caused by a
+ breakpoint. */
/* FIXME drow/2002-06-09: Get signal numbers from the inferior's
thread library? */
if (WIFSTOPPED (w)
&& current_thread->last_resume_kind != resume_step
&& (
#if defined (USE_THREAD_DB) && !defined (__ANDROID__)
- (current_process ()->private->thread_db != NULL
+ (current_process ()->priv->thread_db != NULL
&& (WSTOPSIG (w) == __SIGRTMIN
|| WSTOPSIG (w) == __SIGRTMIN + 1))
||
#endif
(pass_signals[gdb_signal_from_host (WSTOPSIG (w))]
&& !(WSTOPSIG (w) == SIGSTOP
- && current_thread->last_resume_kind == resume_stop))))
+ && current_thread->last_resume_kind == resume_stop)
+ && !linux_wstatus_maybe_breakpoint (w))))
{
siginfo_t info, *info_p;
info_p = &info;
else
info_p = NULL;
- linux_resume_one_lwp (event_child, event_child->stepping,
- WSTOPSIG (w), info_p);
- goto retry;
+
+ if (step_over_finished)
+ {
+ /* We cancelled this thread's step-over above. We still
+ need to unsuspend all other LWPs, and set them back
+ running again while the signal handler runs. */
+ unsuspend_all_lwps (event_child);
+
+ /* Enqueue the pending signal info so that proceed_all_lwps
+ doesn't lose it. */
+ enqueue_pending_signal (event_child, WSTOPSIG (w), info_p);
+
+ proceed_all_lwps ();
+ }
+ else
+ {
+ linux_resume_one_lwp (event_child, event_child->stepping,
+ WSTOPSIG (w), info_p);
+ }
+ return ignore_event (ourstatus);
}
/* Note that all addresses are always "out of the step range" when
do, we're be able to handle GDB breakpoints on top of internal
breakpoints, by handling the internal breakpoint and still
reporting the event to GDB. If we don't, we're out of luck, GDB
- won't see the breakpoint hit. */
+ won't see the breakpoint hit. If we see a single-step event but
+ the thread should be continuing, don't pass the trap to gdb.
+ That indicates that we had previously finished a single-step but
+ left the single-step pending -- see
+ complete_ongoing_step_over. */
report_to_gdb = (!maybe_internal_trap
|| (current_thread->last_resume_kind == resume_step
&& !in_step_range)
- || event_child->stopped_by_watchpoint
- || (!step_over_finished && !in_step_range
- && !bp_explains_trap && !trace_event)
+ || event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
+ || (!in_step_range
+ && !bp_explains_trap
+ && !trace_event
+ && !step_over_finished
+ && !(current_thread->last_resume_kind == resume_continue
+ && event_child->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP))
|| (gdb_breakpoint_here (event_child->stop_pc)
&& gdb_condition_true_at_breakpoint (event_child->stop_pc)
- && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc))
+ || event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE);
run_breakpoint_commands (event_child->stop_pc);
unsuspend_all_lwps (event_child);
proceed_all_lwps ();
- goto retry;
+ return ignore_event (ourstatus);
}
if (debug_threads)
{
+ if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ {
+ char *str;
+
+ str = target_waitstatus_to_string (&event_child->waitstatus);
+ debug_printf ("LWP %ld: extended event with waitstatus %s\n",
+ lwpid_of (get_lwp_thread (event_child)), str);
+ xfree (str);
+ }
if (current_thread->last_resume_kind == resume_step)
{
if (event_child->step_range_start == event_child->step_range_end)
else if (!lwp_in_step_range (event_child))
debug_printf ("Out of step range, reporting event.\n");
}
- if (event_child->stopped_by_watchpoint)
+ if (event_child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
debug_printf ("Stopped by watchpoint.\n");
- if (gdb_breakpoint_here (event_child->stop_pc))
+ else if (gdb_breakpoint_here (event_child->stop_pc))
debug_printf ("Stopped by GDB breakpoint.\n");
if (debug_threads)
debug_printf ("Hit a non-gdbserver trap event.\n");
/* Alright, we're going to report a stop. */
- if (!non_stop && !stabilizing_threads)
+ if (!stabilizing_threads)
{
/* In all-stop, stop all threads. */
- stop_all_lwps (0, NULL);
+ if (!non_stop)
+ stop_all_lwps (0, NULL);
/* If we're not waiting for a specific LWP, choose an event LWP
from among those that have had events. Giving equal priority
w = event_child->status_pending;
}
- /* Now that we've selected our final event LWP, cancel any
- breakpoints in other LWPs that have hit a GDB breakpoint.
- See the comment in cancel_breakpoints_callback to find out
- why. */
- find_inferior (&all_threads, cancel_breakpoints_callback, event_child);
-
- /* If we were going a step-over, all other threads but the stepping one
- had been paused in start_step_over, with their suspend counts
- incremented. We don't want to do a full unstop/unpause, because we're
- in all-stop mode (so we want threads stopped), but we still need to
- unsuspend the other threads, to decrement their `suspended' count
- back. */
if (step_over_finished)
- unsuspend_all_lwps (event_child);
+ {
+ if (!non_stop)
+ {
+ /* If we were doing a step-over, all other threads but
+ the stepping one had been paused in start_step_over,
+ with their suspend counts incremented. We don't want
+ to do a full unstop/unpause, because we're in
+ all-stop mode (so we want threads stopped), but we
+ still need to unsuspend the other threads, to
+ decrement their `suspended' count back. */
+ unsuspend_all_lwps (event_child);
+ }
+ else
+ {
+ /* If we just finished a step-over, then all threads had
+ been momentarily paused. In all-stop, that's fine,
+ we want threads stopped by now anyway. In non-stop,
+ we need to re-resume threads that GDB wanted to be
+ running. */
+ unstop_all_lwps (1, event_child);
+ }
+ }
/* Stabilize threads (move out of jump pads). */
- stabilize_threads ();
+ if (!non_stop)
+ stabilize_threads ();
}
else
{
unstop_all_lwps (1, event_child);
}
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* If the reported event is an exit, fork, vfork or exec, let
+ GDB know. */
+ *ourstatus = event_child->waitstatus;
+ /* Clear the event lwp's waitstatus since we handled it already. */
+ event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ }
+ else
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ /* Now that we've selected our final event LWP, un-adjust its PC if
+ it was a software breakpoint, and the client doesn't know we can
+ adjust the breakpoint ourselves. */
+ if (event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+ && !swbreak_feature)
+ {
+ int decr_pc = the_low_target.decr_pc_after_break;
+
+ if (decr_pc != 0)
+ {
+ struct regcache *regcache
+ = get_thread_regcache (current_thread, 1);
+ (*the_low_target.set_pc) (regcache, event_child->stop_pc + decr_pc);
+ }
+ }
if (current_thread->last_resume_kind == resume_stop
&& WSTOPSIG (w) == SIGSTOP)
but, it stopped for other reasons. */
ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
- else
+ else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
{
ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
debug_exit ();
}
+ if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+ return filter_exit_event (event_child, ourstatus);
+
return ptid_of (current_thread);
}
if (target_is_async_p ())
async_file_flush ();
- event_ptid = linux_wait_1 (ptid, ourstatus, target_options);
+ do
+ {
+ event_ptid = linux_wait_1 (ptid, ourstatus, target_options);
+ }
+ while ((target_options & TARGET_WNOHANG) == 0
+ && ptid_equal (event_ptid, null_ptid)
+ && ourstatus->kind == TARGET_WAITKIND_IGNORE);
/* If at least one stop was reported, there may be more. A single
SIGCHLD can signal more than one child stop. */
if (lwp == except)
return 0;
- lwp->suspended++;
+ lwp_suspended_inc (lwp);
return send_sigstop_callback (entry, except);
}
static void
mark_lwp_dead (struct lwp_info *lwp, int wstat)
{
- /* It's dead, really. */
- lwp->dead = 1;
-
/* Store the exit status for later. */
lwp->status_pending_p = 1;
lwp->status_pending = wstat;
+ /* Store in waitstatus as well, as there's nothing else to process
+ for this event. */
+ if (WIFEXITED (wstat))
+ {
+ lwp->waitstatus.kind = TARGET_WAITKIND_EXITED;
+ lwp->waitstatus.value.integer = WEXITSTATUS (wstat);
+ }
+ else if (WIFSIGNALED (wstat))
+ {
+ lwp->waitstatus.kind = TARGET_WAITKIND_SIGNALLED;
+ lwp->waitstatus.value.sig = gdb_signal_from_host (WTERMSIG (wstat));
+ }
+
/* Prevent trying to stop it. */
lwp->stopped = 1;
lwp->stop_expected = 0;
}
+/* Return true if LWP has exited already, and has a pending exit event
+ to report to GDB. */
+
+static int
+lwp_is_marked_dead (struct lwp_info *lwp)
+{
+ return (lwp->status_pending_p
+ && (WIFEXITED (lwp->status_pending)
+ || WIFSIGNALED (lwp->status_pending)));
+}
+
/* Wait for all children to stop for the SIGSTOPs we just queued. */
static void
if (debug_threads)
debug_printf ("Previously current thread died.\n");
- if (non_stop)
- {
- /* We can't change the current inferior behind GDB's back,
- otherwise, a subsequent command may apply to the wrong
- process. */
- current_thread = NULL;
- }
- else
- {
- /* Set a valid thread as current. */
- set_desired_thread (0);
- }
+ /* We can't change the current inferior behind GDB's back,
+ otherwise, a subsequent command may apply to the wrong
+ process. */
+ current_thread = NULL;
}
}
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
- gdb_assert (lwp->suspended == 0);
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld is suspended, suspended=%d\n",
+ lwpid_of (thread), lwp->suspended);
+ }
gdb_assert (lwp->stopped);
/* Allow debugging the jump pad, gdb_collect, etc.. */
return (supports_fast_tracepoints ()
&& agent_loaded_p ()
&& (gdb_breakpoint_here (lwp->stop_pc)
- || lwp->stopped_by_watchpoint
+ || lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT
|| thread->last_resume_kind == resume_step)
&& linux_fast_tracepoint_collecting (lwp, NULL));
}
move_out_of_jump_pad_callback (struct inferior_list_entry *entry)
{
struct thread_info *thread = (struct thread_info *) entry;
+ struct thread_info *saved_thread;
struct lwp_info *lwp = get_thread_lwp (thread);
int *wstat;
- gdb_assert (lwp->suspended == 0);
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld is suspended, suspended=%d\n",
+ lwpid_of (thread), lwp->suspended);
+ }
gdb_assert (lwp->stopped);
+ /* For gdb_breakpoint_here. */
+ saved_thread = current_thread;
+ current_thread = thread;
+
wstat = lwp->status_pending_p ? &lwp->status_pending : NULL;
/* Allow debugging the jump pad, gdb_collect, etc. */
if (!gdb_breakpoint_here (lwp->stop_pc)
- && !lwp->stopped_by_watchpoint
+ && lwp->stop_reason != TARGET_STOPPED_BY_WATCHPOINT
&& thread->last_resume_kind != resume_step
&& maybe_move_out_of_jump_pad (lwp, wstat))
{
linux_resume_one_lwp (lwp, 0, 0, NULL);
}
else
- lwp->suspended++;
+ lwp_suspended_inc (lwp);
+
+ current_thread = saved_thread;
}
static int
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
- if (lwp->dead)
+ if (lwp_is_marked_dead (lwp))
return 0;
if (lwp->stopped)
return 0;
}
}
-/* Resume execution of the inferior process.
- If STEP is nonzero, single-step it.
- If SIGNAL is nonzero, give it that signal. */
+/* Enqueue one signal in the chain of signals which need to be
+ delivered to this process on next resume. */
static void
-linux_resume_one_lwp (struct lwp_info *lwp,
- int step, int signal, siginfo_t *info)
+enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info)
+{
+ struct pending_signals *p_sig = XNEW (struct pending_signals);
+
+ p_sig->prev = lwp->pending_signals;
+ p_sig->signal = signal;
+ if (info == NULL)
+ memset (&p_sig->info, 0, sizeof (siginfo_t));
+ else
+ memcpy (&p_sig->info, info, sizeof (siginfo_t));
+ lwp->pending_signals = p_sig;
+}
+
+/* Resume execution of LWP. If STEP is nonzero, single-step it. If
+ SIGNAL is nonzero, give it that signal. */
+
+static void
+linux_resume_one_lwp_throw (struct lwp_info *lwp,
+ int step, int signal, siginfo_t *info)
{
struct thread_info *thread = get_lwp_thread (lwp);
struct thread_info *saved_thread;
int fast_tp_collecting;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* Note that target description may not be initialised
+ (proc->tdesc == NULL) at this point because the program hasn't
+ stopped at the first instruction yet. It means GDBserver skips
+ the extra traps from the wrapper program (see option --wrapper).
+ Code in this function that requires register access should be
+ guarded by proc->tdesc == NULL or something else. */
if (lwp->stopped == 0)
return;
+ gdb_assert (lwp->waitstatus.kind == TARGET_WAITKIND_IGNORE);
+
fast_tp_collecting = lwp->collecting_fast_tracepoint;
gdb_assert (!stabilizing_threads || fast_tp_collecting);
/* Cancel actions that rely on GDB not changing the PC (e.g., the
user used the "jump" command, or "set $pc = foo"). */
- if (lwp->stop_pc != get_pc (lwp))
+ if (thread->while_stepping != NULL && lwp->stop_pc != get_pc (lwp))
{
/* Collecting 'while-stepping' actions doesn't make sense
anymore. */
|| lwp->bp_reinsert != 0
|| fast_tp_collecting))
{
- struct pending_signals *p_sig;
- p_sig = xmalloc (sizeof (*p_sig));
+ struct pending_signals *p_sig = XNEW (struct pending_signals);
+
p_sig->prev = lwp->pending_signals;
p_sig->signal = signal;
if (info == NULL)
step = 1;
}
- if (debug_threads && the_low_target.get_pc != NULL)
+ if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
- CORE_ADDR pc = (*the_low_target.get_pc) (regcache);
- debug_printf (" resuming from pc 0x%lx\n", (long) pc);
+
+ lwp->stop_pc = (*the_low_target.get_pc) (regcache);
+
+ if (debug_threads)
+ {
+ debug_printf (" %s from pc 0x%lx\n", step ? "step" : "continue",
+ (long) lwp->stop_pc);
+ }
}
/* If we have pending signals, consume one unless we are trying to
regcache_invalidate_thread (thread);
errno = 0;
- lwp->stopped = 0;
- lwp->stopped_by_watchpoint = 0;
lwp->stepping = step;
ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
(PTRACE_TYPE_ARG3) 0,
current_thread = saved_thread;
if (errno)
+ perror_with_name ("resuming thread");
+
+ /* Successfully resumed. Clear state that no longer makes sense,
+ and mark the LWP as running. Must not do this before resuming
+ otherwise if that fails other code will be confused. E.g., we'd
+ later try to stop the LWP and hang forever waiting for a stop
+ status. Note that we must not throw after this is cleared,
+ otherwise handle_zombie_lwp_error would get confused. */
+ lwp->stopped = 0;
+ lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+}
+
+/* Called when we try to resume a stopped LWP and that errors out. If
+ the LWP is no longer in ptrace-stopped state (meaning it's zombie,
+ or about to become), discard the error, clear any pending status
+ the LWP may have, and return true (we'll collect the exit status
+ soon enough). Otherwise, return false. */
+
+static int
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
+{
+ struct thread_info *thread = get_lwp_thread (lp);
+
+ /* If we get an error after resuming the LWP successfully, we'd
+ confuse !T state for the LWP being gone. */
+ gdb_assert (lp->stopped);
+
+ /* We can't just check whether the LWP is in 'Z (Zombie)' state,
+ because even if ptrace failed with ESRCH, the tracee may be "not
+ yet fully dead", but already refusing ptrace requests. In that
+ case the tracee has 'R (Running)' state for a little bit
+ (observed in Linux 3.18). See also the note on ESRCH in the
+ ptrace(2) man page. Instead, check whether the LWP has any state
+ other than ptrace-stopped. */
+
+ /* Don't assume anything if /proc/PID/status can't be read. */
+ if (linux_proc_pid_is_trace_stopped_nowarn (lwpid_of (thread)) == 0)
{
- /* ESRCH from ptrace either means that the thread was already
- running (an error) or that it is gone (a race condition). If
- it's gone, we will get a notification the next time we wait,
- so we can ignore the error. We could differentiate these
- two, but it's tricky without waiting; the thread still exists
- as a zombie, so sending it signal 0 would succeed. So just
- ignore ESRCH. */
- if (errno == ESRCH)
- return;
+ lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ lp->status_pending_p = 0;
+ return 1;
+ }
+ return 0;
+}
- perror_with_name ("ptrace");
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+ disappears while we try to resume it. */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lwp,
+ int step, int signal, siginfo_t *info)
+{
+ TRY
+ {
+ linux_resume_one_lwp_throw (lwp, step, signal, info);
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (!check_ptrace_stopped_lwp_gone (lwp))
+ throw_exception (ex);
}
+ END_CATCH
}
struct thread_resume_array
int ndx;
struct thread_resume_array *r;
- r = arg;
+ r = (struct thread_resume_array *) arg;
for (ndx = 0; ndx < r->n; ndx++)
{
if (lwp->resume == NULL)
return 0;
- if (lwp->status_pending_p)
+ if (thread_still_has_status_pending_p (thread))
* (int *) flag_p = 1;
return 0;
struct lwp_info *lwp = get_thread_lwp (thread);
struct thread_info *saved_thread;
CORE_ADDR pc;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* GDBserver is skipping the extra traps from the wrapper program,
+ don't have to do step over. */
+ if (proc->tdesc == NULL)
+ return 0;
/* LWPs which will not be resumed are not interesting, because we
might not wait for them next time through linux_wait. */
lwpid_of (thread));
stop_all_lwps (1, lwp);
- gdb_assert (lwp->suspended == 0);
+
+ if (lwp->suspended != 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "LWP %ld suspended=%d\n", lwpid_of (thread),
+ lwp->suspended);
+ }
if (debug_threads)
debug_printf ("Done stopping all threads for step-over.\n");
return 0;
}
+/* If there's a step over in progress, wait until all threads stop
+ (that is, until the stepping thread finishes its step), and
+ unsuspend all lwps. The stepping thread ends with its status
+ pending, which is processed later when we get back to processing
+ events. */
+
+static void
+complete_ongoing_step_over (void)
+{
+ if (!ptid_equal (step_over_bkpt, null_ptid))
+ {
+ struct lwp_info *lwp;
+ int wstat;
+ int ret;
+
+ if (debug_threads)
+ debug_printf ("detach: step over in progress, finish it first\n");
+
+ /* Passing NULL_PTID as filter indicates we want all events to
+ be left pending. Eventually this returns when there are no
+ unwaited-for children left. */
+ ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
+ &wstat, __WALL);
+ gdb_assert (ret == -1);
+
+ lwp = find_lwp_pid (step_over_bkpt);
+ if (lwp != NULL)
+ finish_step_over (lwp);
+ step_over_bkpt = null_ptid;
+ unsuspend_all_lwps (lwp);
+ }
+}
+
/* This function is called once per thread. We check the thread's resume
request, which will tell us whether to resume, step, or leave the thread
stopped; and what signal, if any, it should be sent.
}
/* If this thread which is about to be resumed has a pending status,
- then don't resume any threads - we can just report the pending
- status. Make sure to queue any signals that would otherwise be
- sent. In all-stop mode, we do this decision based on if *any*
- thread has a pending status. If there's a thread that needs the
- step-over-breakpoint dance, then don't resume any other thread
- but that particular one. */
- leave_pending = (lwp->status_pending_p || leave_all_stopped);
+ then don't resume it - we can just report the pending status.
+ Likewise if it is suspended, because e.g., another thread is
+ stepping past a breakpoint. Make sure to queue any signals that
+ would otherwise be sent. In all-stop mode, we do this decision
+ based on if *any* thread has a pending status. If there's a
+ thread that needs the step-over-breakpoint dance, then don't
+ resume any other thread but that particular one. */
+ leave_pending = (lwp->suspended
+ || lwp->status_pending_p
+ || leave_all_stopped);
if (!leave_pending)
{
/* If we have a new signal, enqueue the signal. */
if (lwp->resume->sig != 0)
{
- struct pending_signals *p_sig;
- p_sig = xmalloc (sizeof (*p_sig));
+ struct pending_signals *p_sig = XCNEW (struct pending_signals);
+
p_sig->prev = lwp->pending_signals;
p_sig->signal = lwp->resume->sig;
- memset (&p_sig->info, 0, sizeof (siginfo_t));
/* If this is the same signal we were previously stopped by,
make sure to queue its siginfo. We can ignore the return
send_sigstop (lwp);
}
- step = thread->last_resume_kind == resume_step;
+ if (thread->last_resume_kind == resume_step)
+ {
+ if (debug_threads)
+ debug_printf (" stepping LWP %ld, client wants it stepping\n",
+ lwpid_of (thread));
+ step = 1;
+ }
+ else if (lwp->bp_reinsert != 0)
+ {
+ if (debug_threads)
+ debug_printf (" stepping LWP %ld, reinsert set\n",
+ lwpid_of (thread));
+ step = 1;
+ }
+ else
+ step = 0;
+
linux_resume_one_lwp (lwp, step, 0, NULL);
return 0;
}
if (lwp == except)
return 0;
- lwp->suspended--;
- gdb_assert (lwp->suspended >= 0);
+ lwp_suspended_decr (lwp);
return proceed_one_lwp (entry, except);
}
dr_offset = regset - info->regsets;
if (info->disabled_regsets == NULL)
- info->disabled_regsets = xcalloc (1, info->num_regsets);
+ info->disabled_regsets = (char *) xcalloc (1, info->num_regsets);
info->disabled_regsets[dr_offset] = 1;
}
size = ((register_size (regcache->tdesc, regno)
+ sizeof (PTRACE_XFER_TYPE) - 1)
& -sizeof (PTRACE_XFER_TYPE));
- buf = alloca (size);
+ buf = (char *) alloca (size);
pid = lwpid_of (current_thread);
for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
size = ((register_size (regcache->tdesc, regno)
+ sizeof (PTRACE_XFER_TYPE) - 1)
& -sizeof (PTRACE_XFER_TYPE));
- buf = alloca (size);
+ buf = (char *) alloca (size);
memset (buf, 0, size);
if (the_low_target.collect_ptrace_register)
count = ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
/ sizeof (PTRACE_XFER_TYPE));
/* Allocate buffer of that many longwords. */
- buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+ buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
/* Read all the longwords */
errno = 0;
/ sizeof (PTRACE_XFER_TYPE);
/* Allocate buffer of that many longwords. */
- register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *)
- alloca (count * sizeof (PTRACE_XFER_TYPE));
+ register PTRACE_XFER_TYPE *buffer = XALLOCAVEC (PTRACE_XFER_TYPE, count);
int pid = lwpid_of (current_thread);
if (debug_threads)
{
/* Dump up to four bytes. */
- unsigned int val = * (unsigned int *) myaddr;
- if (len == 1)
- val = val & 0xff;
- else if (len == 2)
- val = val & 0xffff;
- else if (len == 3)
- val = val & 0xffffff;
- debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
- val, (long)memaddr);
+ char str[4 * 2 + 1];
+ char *p = str;
+ int dump = len < 4 ? len : 4;
+
+ for (i = 0; i < dump; i++)
+ {
+ sprintf (p, "%02x", myaddr[i]);
+ p += 2;
+ }
+ *p = '\0';
+
+ debug_printf ("Writing %s to 0x%08lx in process %d\n",
+ str, (long) memaddr, pid);
}
/* Fill start and end extra bytes of buffer with existing memory data. */
#ifdef USE_THREAD_DB
struct process_info *proc = current_process ();
- if (proc->private->thread_db != NULL)
+ if (proc->priv->thread_db != NULL)
return;
/* If the kernel supports tracing clones, then we don't need to
linux_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
int size, struct raw_breakpoint *bp)
{
- if (the_low_target.insert_point != NULL)
+ if (type == raw_bkpt_type_sw)
+ return insert_memory_breakpoint (bp);
+ else if (the_low_target.insert_point != NULL)
return the_low_target.insert_point (type, addr, size, bp);
else
/* Unsupported (see target.h). */
linux_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
int size, struct raw_breakpoint *bp)
{
- if (the_low_target.remove_point != NULL)
+ if (type == raw_bkpt_type_sw)
+ return remove_memory_breakpoint (bp);
+ else if (the_low_target.remove_point != NULL)
return the_low_target.remove_point (type, addr, size, bp);
else
/* Unsupported (see target.h). */
return 1;
}
+/* Implement the to_stopped_by_sw_breakpoint target_ops
+ method. */
+
+static int
+linux_stopped_by_sw_breakpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_sw_breakpoint target_ops
+ method. */
+
+static int
+linux_supports_stopped_by_sw_breakpoint (void)
+{
+ return USE_SIGTRAP_SIGINFO;
+}
+
+/* Implement the to_stopped_by_hw_breakpoint target_ops
+ method. */
+
+static int
+linux_stopped_by_hw_breakpoint (void)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+ return (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_hw_breakpoint target_ops
+ method. */
+
+static int
+linux_supports_stopped_by_hw_breakpoint (void)
+{
+ return USE_SIGTRAP_SIGINFO;
+}
+
+/* Implement the supports_hardware_single_step target_ops method. */
+
+static int
+linux_supports_hardware_single_step (void)
+{
+ return can_hardware_single_step ();
+}
+
static int
linux_stopped_by_watchpoint (void)
{
struct lwp_info *lwp = get_thread_lwp (current_thread);
- return lwp->stopped_by_watchpoint;
+ return lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
}
static CORE_ADDR
linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
{
unsigned long text, text_end, data;
- int pid = lwpid_of (get_thread_lwp (current_thread));
+ int pid = lwpid_of (current_thread);
errno = 0;
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported. */
+
+static int
+linux_supports_exec_events (void)
+{
+ return linux_supports_traceexec ();
+}
+
+/* Callback for 'find_inferior'. Set the (possibly changed) ptrace
+ options for the specified lwp. */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+ void *args)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (!lwp->stopped)
+ {
+ /* Stop the lwp so we can modify its ptrace options. */
+ lwp->must_set_ptrace_flags = 1;
+ linux_stop_lwp (lwp);
+ }
+ else
+ {
+ /* Already stopped; go ahead and set the ptrace options. */
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
+
+ linux_enable_event_reporting (lwpid_of (thread), options);
+ lwp->must_set_ptrace_flags = 0;
+ }
+
+ return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the
+ ptrace flags for all inferiors. This is in case the new GDB connection
+ doesn't support the same set of events that the previous one did. */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+ pid_t pid;
+
+ /* Request that all the lwps reset their ptrace options. */
+ find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
static int
linux_supports_disable_randomization (void)
{
#endif /* defined PT_GETDSBT || defined PTRACE_GETFDPIC */
static void
-linux_process_qsupported (const char *query)
+linux_process_qsupported (char **features, int count)
{
if (the_low_target.process_qsupported != NULL)
- the_low_target.process_qsupported (query);
+ the_low_target.process_qsupported (features, count);
}
static int
return 0;
gdb_assert (num_phdr < 100); /* Basic sanity check. */
- phdr_buf = alloca (num_phdr * phdr_size);
+ phdr_buf = (unsigned char *) alloca (num_phdr * phdr_size);
if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size))
return 0;
if (is_elf64)
{
Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
-#ifdef DT_MIPS_RLD_MAP
+#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
union
{
Elf64_Xword map;
unsigned char buf[sizeof (Elf64_Xword)];
}
rld_map;
-
+#endif
+#ifdef DT_MIPS_RLD_MAP
if (dyn->d_tag == DT_MIPS_RLD_MAP)
{
if (linux_read_memory (dyn->d_un.d_val,
break;
}
#endif /* DT_MIPS_RLD_MAP */
+#ifdef DT_MIPS_RLD_MAP_REL
+ if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
+ {
+ if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP_REL */
if (dyn->d_tag == DT_DEBUG && map == -1)
map = dyn->d_un.d_val;
else
{
Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
-#ifdef DT_MIPS_RLD_MAP
+#if defined DT_MIPS_RLD_MAP || defined DT_MIPS_RLD_MAP_REL
union
{
Elf32_Word map;
unsigned char buf[sizeof (Elf32_Word)];
}
rld_map;
-
+#endif
+#ifdef DT_MIPS_RLD_MAP
if (dyn->d_tag == DT_MIPS_RLD_MAP)
{
if (linux_read_memory (dyn->d_un.d_val,
break;
}
#endif /* DT_MIPS_RLD_MAP */
+#ifdef DT_MIPS_RLD_MAP_REL
+ if (dyn->d_tag == DT_MIPS_RLD_MAP_REL)
+ {
+ if (linux_read_memory (dyn->d_un.d_val + dynamic_memaddr,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP_REL */
if (dyn->d_tag == DT_DEBUG && map == -1)
map = dyn->d_un.d_val;
{
char *document;
unsigned document_len;
- struct process_info_private *const priv = current_process ()->private;
+ struct process_info_private *const priv = current_process ()->priv;
char filename[PATH_MAX];
int pid, is_elf64;
break;
len = sep - annex;
- if (len == 5 && strncmp (annex, "start", 5) == 0)
+ if (len == 5 && startswith (annex, "start"))
addrp = &lm_addr;
- else if (len == 4 && strncmp (annex, "prev", 4) == 0)
+ else if (len == 4 && startswith (annex, "prev"))
addrp = &lm_prev;
else
{
}
}
- document = xmalloc (allocated);
+ document = (char *) xmalloc (allocated);
strcpy (document, "<library-list-svr4 version=\"1.0\"");
p = document + strlen (document);
/* Expand to guarantee sufficient storage. */
uintptr_t document_len = p - document;
- document = xrealloc (document, 2 * allocated);
+ document = (char *) xrealloc (document, 2 * allocated);
allocated *= 2;
p = document + document_len;
}
#ifdef HAVE_LINUX_BTRACE
-/* See to_enable_btrace target method. */
+/* See to_disable_btrace target method. */
-static struct btrace_target_info *
-linux_low_enable_btrace (ptid_t ptid)
+static int
+linux_low_disable_btrace (struct btrace_target_info *tinfo)
{
- struct btrace_target_info *tinfo;
+ enum btrace_error err;
+
+ err = linux_disable_btrace (tinfo);
+ return (err == BTRACE_ERR_NONE ? 0 : -1);
+}
+
+/* Encode an Intel(R) Processor Trace configuration. */
- tinfo = linux_enable_btrace (ptid);
+static void
+linux_low_encode_pt_config (struct buffer *buffer,
+ const struct btrace_data_pt_config *config)
+{
+ buffer_grow_str (buffer, "<pt-config>\n");
- if (tinfo != NULL)
+ switch (config->cpu.vendor)
{
- struct thread_info *thread = find_thread_ptid (ptid);
- struct regcache *regcache = get_thread_regcache (thread, 0);
+ case CV_INTEL:
+ buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
+ "model=\"%u\" stepping=\"%u\"/>\n",
+ config->cpu.family, config->cpu.model,
+ config->cpu.stepping);
+ break;
- tinfo->ptr_bits = register_size (regcache->tdesc, 0) * 8;
+ default:
+ break;
}
- return tinfo;
+ buffer_grow_str (buffer, "</pt-config>\n");
}
-/* See to_disable_btrace target method. */
+/* Encode a raw buffer. */
-static int
-linux_low_disable_btrace (struct btrace_target_info *tinfo)
+static void
+linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
+ unsigned int size)
{
- enum btrace_error err;
+ if (size == 0)
+ return;
- err = linux_disable_btrace (tinfo);
- return (err == BTRACE_ERR_NONE ? 0 : -1);
+ /* We use hex encoding - see common/rsp-low.h. */
+ buffer_grow_str (buffer, "<raw>\n");
+
+ while (size-- > 0)
+ {
+ char elem[2];
+
+ elem[0] = tohex ((*data >> 4) & 0xf);
+ elem[1] = tohex (*data++ & 0xf);
+
+ buffer_grow (buffer, elem, 2);
+ }
+
+ buffer_grow_str (buffer, "</raw>\n");
}
/* See to_read_btrace target method. */
static int
linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
- int type)
+ enum btrace_read_type type)
{
- VEC (btrace_block_s) *btrace;
+ struct btrace_data btrace;
struct btrace_block *block;
enum btrace_error err;
int i;
- btrace = NULL;
+ btrace_data_init (&btrace);
+
err = linux_read_btrace (&btrace, tinfo, type);
if (err != BTRACE_ERR_NONE)
{
else
buffer_grow_str0 (buffer, "E.Generic Error.");
- return -1;
+ goto err;
+ }
+
+ switch (btrace.format)
+ {
+ case BTRACE_FORMAT_NONE:
+ buffer_grow_str0 (buffer, "E.No Trace.");
+ goto err;
+
+ case BTRACE_FORMAT_BTS:
+ buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+
+ for (i = 0;
+ VEC_iterate (btrace_block_s, btrace.variant.bts.blocks, i, block);
+ i++)
+ buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
+ paddress (block->begin), paddress (block->end));
+
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
+
+ case BTRACE_FORMAT_PT:
+ buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+ buffer_grow_str (buffer, "<pt>\n");
+
+ linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
+
+ linux_low_encode_raw (buffer, btrace.variant.pt.data,
+ btrace.variant.pt.size);
+
+ buffer_grow_str (buffer, "</pt>\n");
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
+
+ default:
+ buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
+ goto err;
}
- buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
- buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+ btrace_data_fini (&btrace);
+ return 0;
+
+err:
+ btrace_data_fini (&btrace);
+ return -1;
+}
+
+/* See to_btrace_conf target method. */
+
+static int
+linux_low_btrace_conf (const struct btrace_target_info *tinfo,
+ struct buffer *buffer)
+{
+ const struct btrace_config *conf;
+
+ buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
- for (i = 0; VEC_iterate (btrace_block_s, btrace, i, block); i++)
- buffer_xml_printf (buffer, "<block begin=\"0x%s\" end=\"0x%s\"/>\n",
- paddress (block->begin), paddress (block->end));
+ conf = linux_btrace_conf (tinfo);
+ if (conf != NULL)
+ {
+ switch (conf->format)
+ {
+ case BTRACE_FORMAT_NONE:
+ break;
- buffer_grow_str0 (buffer, "</btrace>\n");
+ case BTRACE_FORMAT_BTS:
+ buffer_xml_printf (buffer, "<bts");
+ buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
+ buffer_xml_printf (buffer, " />\n");
+ break;
- VEC_free (btrace_block_s, btrace);
+ case BTRACE_FORMAT_PT:
+ buffer_xml_printf (buffer, "<pt");
+ buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
+ buffer_xml_printf (buffer, "/>\n");
+ break;
+ }
+ }
+ buffer_grow_str0 (buffer, "</btrace-conf>\n");
return 0;
}
#endif /* HAVE_LINUX_BTRACE */
+/* See nat/linux-nat.h. */
+
+ptid_t
+current_lwp_ptid (void)
+{
+ return ptid_of (current_thread);
+}
+
+/* Implementation of the target_ops method "breakpoint_kind_from_pc". */
+
+static int
+linux_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (the_low_target.breakpoint_kind_from_pc != NULL)
+ return (*the_low_target.breakpoint_kind_from_pc) (pcptr);
+ else
+ return default_breakpoint_kind_from_pc (pcptr);
+}
+
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+linux_sw_breakpoint_from_kind (int kind, int *size)
+{
+ gdb_assert (the_low_target.sw_breakpoint_from_kind != NULL);
+
+ return (*the_low_target.sw_breakpoint_from_kind) (kind, size);
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
+ linux_arch_setup,
linux_attach,
linux_kill,
linux_detach,
linux_supports_z_point_type,
linux_insert_point,
linux_remove_point,
+ linux_stopped_by_sw_breakpoint,
+ linux_supports_stopped_by_sw_breakpoint,
+ linux_stopped_by_hw_breakpoint,
+ linux_supports_stopped_by_hw_breakpoint,
+ linux_supports_hardware_single_step,
linux_stopped_by_watchpoint,
linux_stopped_data_address,
#if defined(__UCLIBC__) && defined(HAS_NOMMU) \
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
+ linux_supports_exec_events,
+ linux_handle_new_gdb_connection,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
NULL,
linux_pause_all,
linux_unpause_all,
- linux_cancel_breakpoints,
linux_stabilize_threads,
linux_install_fast_tracepoint_jump_pad,
linux_emit_ops,
linux_supports_agent,
#ifdef HAVE_LINUX_BTRACE
linux_supports_btrace,
- linux_low_enable_btrace,
+ linux_enable_btrace,
linux_low_disable_btrace,
linux_low_read_btrace,
+ linux_low_btrace_conf,
#else
NULL,
NULL,
NULL,
NULL,
+ NULL,
#endif
linux_supports_range_stepping,
+ linux_proc_pid_to_exec_file,
+ linux_mntns_open_cloexec,
+ linux_mntns_unlink,
+ linux_mntns_readlink,
+ linux_breakpoint_kind_from_pc,
+ linux_sw_breakpoint_from_kind,
+ linux_proc_tid_get_name,
};
static void
initialize_low (void)
{
struct sigaction sigchld_action;
+
memset (&sigchld_action, 0, sizeof (sigchld_action));
set_target_ops (&linux_target_ops);
- set_breakpoint_data (the_low_target.breakpoint,
- the_low_target.breakpoint_len);
+
linux_init_signals ();
linux_ptrace_init_warnings ();
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ linux_check_ptrace_features ();
}