#include <dirent.h>
#include <sys/stat.h>
#include <sys/vfs.h>
+#include <sys/uio.h>
#ifndef ELFMAG0
/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
then ELFMAG0 will have been defined. If it didn't get included by
return proc;
}
-/* Remove a process from the common process list,
- also freeing all private data. */
-
-static void
-linux_remove_process (struct process_info *process)
-{
- struct process_info_private *priv = process->private;
-
- free (priv->arch_private);
- free (priv);
- remove_process (process);
-}
-
/* Wrapper function for waitpid which handles EINTR, and emulates
__WALL for systems where that is not available. */
lwp->head.id = ptid;
- lwp->last_resume_kind = resume_continue;
-
if (the_low_target.new_thread != NULL)
lwp->arch_private = the_low_target.new_thread ();
of a new thread that is being created.
In this case we should ignore that SIGSTOP and resume the
process. This is handled below by setting stop_expected = 1,
- and the fact that add_lwp sets last_resume_kind ==
+ and the fact that add_thread sets last_resume_kind ==
resume_continue.
2) This is the first thread (the process thread), and we're attaching
int
linux_attach (unsigned long pid)
{
- struct lwp_info *lwp;
-
linux_attach_lwp_1 (pid, 1);
-
linux_add_process (pid, 1);
if (!non_stop)
{
- /* Don't ignore the initial SIGSTOP if we just attached to this
- process. It will be collected by wait shortly. */
- lwp = (struct lwp_info *) find_inferior_id (&all_lwps,
- ptid_build (pid, pid, 0));
- lwp->last_resume_kind = resume_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;
}
return 0;
lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
} while (lwpid > 0 && WIFSTOPPED (wstat));
-#ifdef USE_THREAD_DB
- thread_db_free (process, 0);
-#endif
delete_lwp (lwp);
- linux_remove_process (process);
+
+ the_target->mourn (process);
return 0;
}
return -1;
#ifdef USE_THREAD_DB
- thread_db_free (process, 1);
+ thread_db_detach (process);
#endif
current_inferior =
delete_all_breakpoints ();
find_inferior (&all_threads, linux_detach_one_lwp, &pid);
- linux_remove_process (process);
+
+ the_target->mourn (process);
return 0;
}
+static void
+linux_mourn (struct process_info *process)
+{
+ struct process_info_private *priv;
+
+#ifdef USE_THREAD_DB
+ thread_db_mourn (process);
+#endif
+
+ /* Freeing all private data. */
+ priv = process->private;
+ free (priv->arch_private);
+ free (priv);
+ process->private = NULL;
+
+ remove_process (process);
+}
+
static void
linux_join (int pid)
{
/* If we got a `vCont;t', but we haven't reported a stop yet, do
report any status pending the LWP may have. */
- if (lwp->last_resume_kind == resume_stop
+ if (thread->last_resume_kind == resume_stop
&& thread->last_status.kind == TARGET_WAITKIND_STOPPED)
return 0;
return child;
}
+/* This function should only be called if the LWP got a SIGTRAP.
+
+ Handle any tracepoint steps or hits. Return true if a tracepoint
+ event was handled, 0 otherwise. */
+
+static int
+handle_tracepoints (struct lwp_info *lwp)
+{
+ struct thread_info *tinfo = get_lwp_thread (lwp);
+ int tpoint_related_event = 0;
+
+ /* 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
+ inferior (LWP could be in the jump pad, maybe even holding the
+ lock.) */
+
+ /* Do any necessary step collect actions. */
+ tpoint_related_event |= tracepoint_finished_step (tinfo, lwp->stop_pc);
+
+ /* See if we just hit a tracepoint and do its main collect
+ actions. */
+ tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
+
+ if (tpoint_related_event)
+ {
+ if (debug_threads)
+ fprintf (stderr, "got a tracepoint event\n");
+ return 1;
+ }
+
+ 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,
fprintf (stderr, "Expected stop.\n");
event_child->stop_expected = 0;
- should_stop = (event_child->last_resume_kind == resume_stop
+ should_stop = (current_inferior->last_resume_kind == resume_stop
|| stopping_threads);
if (!should_stop)
count_events_callback (struct inferior_list_entry *entry, void *data)
{
struct lwp_info *lp = (struct lwp_info *) entry;
+ struct thread_info *thread = get_lwp_thread (lp);
int *count = data;
gdb_assert (count != NULL);
/* Count only resumed LWPs that have a SIGTRAP event pending that
should be reported to GDB. */
- if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
- && lp->last_resume_kind != resume_stop
+ if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+ && thread->last_resume_kind != resume_stop
&& lp->status_pending_p
&& WIFSTOPPED (lp->status_pending)
&& WSTOPSIG (lp->status_pending) == SIGTRAP
select_singlestep_lwp_callback (struct inferior_list_entry *entry, void *data)
{
struct lwp_info *lp = (struct lwp_info *) entry;
+ struct thread_info *thread = get_lwp_thread (lp);
- if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
- && lp->last_resume_kind == resume_step
+ if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+ && thread->last_resume_kind == resume_step
&& lp->status_pending_p)
return 1;
else
select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
{
struct lwp_info *lp = (struct lwp_info *) entry;
+ struct thread_info *thread = get_lwp_thread (lp);
int *selector = data;
gdb_assert (selector != NULL);
/* Select only resumed LWPs that have a SIGTRAP event pending. */
- if (lp->last_resume_kind != resume_stop
- && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+ if (thread->last_resume_kind != resume_stop
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE
&& lp->status_pending_p
&& WIFSTOPPED (lp->status_pending)
&& WSTOPSIG (lp->status_pending) == SIGTRAP
cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data)
{
struct lwp_info *lp = (struct lwp_info *) entry;
+ struct thread_info *thread = get_lwp_thread (lp);
struct lwp_info *event_lp = data;
/* Leave the LWP that has been elected to receive a SIGTRAP alone. */
delete or disable the breakpoint, but the LWP will have already
tripped on it. */
- if (lp->last_resume_kind != resume_stop
- && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+ if (thread->last_resume_kind != resume_stop
+ && thread->last_status.kind == TARGET_WAITKIND_IGNORE
&& lp->status_pending_p
&& WIFSTOPPED (lp->status_pending)
&& WSTOPSIG (lp->status_pending) == SIGTRAP
thread->last_status.kind = TARGET_WAITKIND_STOPPED;
thread->last_status.value.sig = TARGET_SIGNAL_0;
- lwp->last_resume_kind = resume_stop;
+ thread->last_resume_kind = resume_stop;
}
/* Set all LWP's states as "want-stopped". */
int bp_explains_trap;
int maybe_internal_trap;
int report_to_gdb;
+ int trace_event;
/* Translate generic target options into linux options. */
options = __WALL;
{
if (WIFEXITED (w) || WIFSIGNALED (w))
{
- int pid = pid_of (event_child);
- struct process_info *process = find_process_pid (pid);
-
-#ifdef USE_THREAD_DB
- thread_db_free (process, 0);
-#endif
delete_lwp (event_child);
- linux_remove_process (process);
current_inferior = NULL;
/* Now invoke the callbacks of any internal breakpoints there. */
check_breakpoints (event_child->stop_pc);
+ /* Handle tracepoint data collecting. This may overflow the
+ trace buffer, and cause a tracing stop, removing
+ breakpoints. */
+ trace_event = handle_tracepoints (event_child);
+
if (bp_explains_trap)
{
/* If we stepped or ran into an internal breakpoint, we've
/* We have some other signal, possibly a step-over dance was in
progress, and it should be cancelled too. */
step_over_finished = finish_step_over (event_child);
+
+ trace_event = 0;
}
/* We have all the data we need. Either report the event to GDB, or
breakpoint and still reporting the event to GDB. If we don't,
we're out of luck, GDB won't see the breakpoint hit. */
report_to_gdb = (!maybe_internal_trap
- || event_child->last_resume_kind == resume_step
+ || current_inferior->last_resume_kind == resume_step
|| event_child->stopped_by_watchpoint
- || (!step_over_finished && !bp_explains_trap)
+ || (!step_over_finished && !bp_explains_trap && !trace_event)
|| gdb_breakpoint_here (event_child->stop_pc));
/* We found no reason GDB would want us to stop. We either hit one
fprintf (stderr, "Hit a gdbserver breakpoint.\n");
if (step_over_finished)
fprintf (stderr, "Step-over finished.\n");
+ if (trace_event)
+ fprintf (stderr, "Tracepoint event.\n");
}
/* We're not reporting this breakpoint to GDB, so apply the
if (debug_threads)
{
- if (event_child->last_resume_kind == resume_step)
+ if (current_inferior->last_resume_kind == resume_step)
fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
if (event_child->stopped_by_watchpoint)
fprintf (stderr, "Stopped by watchpoint.\n");
/* Do this before the gdb_wants_all_stopped calls below, since they
always set last_resume_kind to resume_stop. */
- if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP)
+ if (current_inferior->last_resume_kind == resume_stop
+ && WSTOPSIG (w) == SIGSTOP)
{
/* A thread that has been requested to stop by GDB with vCont;t,
and it stopped cleanly, so report as SIG0. The use of
SIGSTOP is an implementation detail. */
ourstatus->value.sig = TARGET_SIGNAL_0;
}
- else if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) != SIGSTOP)
+ else if (current_inferior->last_resume_kind == resume_stop
+ && WSTOPSIG (w) != SIGSTOP)
{
/* A thread that has been requested to stop by GDB with vCont;t,
but, it stopped for other reasons. */
if (lwp->stopped == 0)
return;
+ /* 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))
+ {
+ /* Collecting 'while-stepping' actions doesn't make sense
+ anymore. */
+ release_while_stepping_state_list (get_lwp_thread (lwp));
+ }
+
/* If we have pending signals or status, and a new signal, enqueue the
signal. Also enqueue the signal if we are waiting to reinsert a
breakpoint; it will be picked up again below. */
signal = 0;
}
+ /* If we have while-stepping actions in this thread set it stepping.
+ If we have a signal to deliver, it may or may not be set to
+ SIG_IGN, we don't know. Assume so, and allow collecting
+ while-stepping into a signal handler. A possible smart thing to
+ do would be to set an internal breakpoint at the signal return
+ address, continue, and carry on catching this while-stepping
+ action only when that breakpoint is hit. A future
+ enhancement. */
+ if (get_lwp_thread (lwp)->while_stepping != NULL
+ && can_hardware_single_step ())
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "lwp %ld has a while-stepping action -> forcing step.\n",
+ lwpid_of (lwp));
+ step = 1;
+ }
+
if (debug_threads && the_low_target.get_pc != NULL)
{
struct regcache *regcache = get_thread_regcache (current_inferior, 1);
&& (ptid_get_pid (ptid) == pid_of (lwp))))
{
if (r->resume[ndx].kind == resume_stop
- && lwp->last_resume_kind == resume_stop)
+ && thread->last_resume_kind == resume_stop)
{
if (debug_threads)
fprintf (stderr, "already %s LWP %ld at GDB's request\n",
}
lwp->resume = &r->resume[ndx];
- lwp->last_resume_kind = lwp->resume->kind;
+ thread->last_resume_kind = lwp->resume->kind;
return 0;
}
}
need_step_over_p (struct inferior_list_entry *entry, void *dummy)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
+ struct thread_info *thread;
struct thread_info *saved_inferior;
CORE_ADDR pc;
return 0;
}
- if (lwp->last_resume_kind == resume_stop)
+ thread = get_lwp_thread (lwp);
+
+ if (thread->last_resume_kind == resume_stop)
{
if (debug_threads)
fprintf (stderr,
}
saved_inferior = current_inferior;
- current_inferior = get_lwp_thread (lwp);
+ current_inferior = thread;
/* We can only step over breakpoints we know about. */
if (breakpoint_here (pc))
proceed_one_lwp (struct inferior_list_entry *entry)
{
struct lwp_info *lwp;
+ struct thread_info *thread;
int step;
lwp = (struct lwp_info *) entry;
return;
}
- if (lwp->last_resume_kind == resume_stop)
+ thread = get_lwp_thread (lwp);
+
+ if (thread->last_resume_kind == resume_stop)
{
if (debug_threads)
fprintf (stderr, " client wants LWP %ld stopped\n", lwpid_of (lwp));
return;
}
- step = lwp->last_resume_kind == resume_step;
+ step = thread->last_resume_kind == resume_step;
linux_resume_one_lwp (lwp, step, 0, NULL);
}
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
+ struct iovec iov;
regset = target_regsets;
pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
{
- void *buf;
- int res;
+ void *buf, *data;
+ int nt_type, res;
if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
}
buf = xmalloc (regset->size);
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, 0, buf);
+ res = ptrace (regset->get_request, pid, nt_type, data);
#else
- res = ptrace (regset->get_request, pid, buf, 0);
+ res = ptrace (regset->get_request, pid, data, nt_type);
#endif
if (res < 0)
{
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
+ struct iovec iov;
regset = target_regsets;
pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
{
- void *buf;
- int res;
+ void *buf, *data;
+ int nt_type, res;
if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
/* First fill the buffer with the current register set contents,
in case there are any items in the kernel's regset that are
not in gdbserver's regcache. */
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, 0, buf);
+ res = ptrace (regset->get_request, pid, nt_type, data);
#else
- res = ptrace (regset->get_request, pid, buf, 0);
+ res = ptrace (regset->get_request, pid, &iov, data);
#endif
if (res == 0)
/* Only now do we write the register set. */
#ifndef __sparc__
- res = ptrace (regset->set_request, pid, 0, buf);
+ res = ptrace (regset->set_request, pid, nt_type, data);
#else
- res = ptrace (regset->set_request, pid, buf, 0);
+ res = ptrace (regset->set_request, pid, data, nt_type);
#endif
}
{
int previous = (linux_event_pipe[0] != -1);
+ if (debug_threads)
+ fprintf (stderr, "linux_async (%d), previous=%d\n",
+ enable, previous);
+
if (previous != enable)
{
sigset_t mask;
return core;
}
+static void
+linux_process_qsupported (const char *query)
+{
+ if (the_low_target.process_qsupported != NULL)
+ the_low_target.process_qsupported (query);
+}
+
+static int
+linux_supports_tracepoints (void)
+{
+ if (*the_low_target.supports_tracepoints == NULL)
+ return 0;
+
+ return (*the_low_target.supports_tracepoints) ();
+}
+
+static CORE_ADDR
+linux_read_pc (struct regcache *regcache)
+{
+ if (the_low_target.get_pc == NULL)
+ return 0;
+
+ return (*the_low_target.get_pc) (regcache);
+}
+
+static void
+linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ gdb_assert (the_low_target.set_pc != NULL);
+
+ (*the_low_target.set_pc) (regcache, pc);
+}
+
+static int
+linux_thread_stopped (struct thread_info *thread)
+{
+ return get_thread_lwp (thread)->stopped;
+}
+
+/* This exposes stop-all-threads functionality to other modules. */
+
+static void
+linux_pause_all (void)
+{
+ stop_all_lwps ();
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
linux_kill,
linux_detach,
+ linux_mourn,
linux_join,
linux_thread_alive,
linux_resume,
#else
NULL,
#endif
- linux_core_of_thread
+ linux_core_of_thread,
+ linux_process_qsupported,
+ linux_supports_tracepoints,
+ linux_read_pc,
+ linux_write_pc,
+ linux_thread_stopped,
+ linux_pause_all,
+ NULL, /* get_tib_address (Windows OS specific). */
};
static void