aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver/linux-low.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbserver/linux-low.c')
-rw-r--r--gdb/gdbserver/linux-low.c204
1 files changed, 161 insertions, 43 deletions
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 36930fe..4a19db7 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -139,14 +139,14 @@ static int new_inferior;
static void linux_resume_one_lwp (struct lwp_info *lwp,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
-static void stop_all_lwps (void);
+static void stop_all_lwps (int suspend, struct lwp_info *except);
+static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
static int linux_core_of_thread (ptid_t ptid);
static void proceed_all_lwps (void);
-static void unstop_all_lwps (struct lwp_info *except);
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);
@@ -765,7 +765,7 @@ linux_kill (int pid)
/* If we're killing a running inferior, make sure it is stopped
first, as PTRACE_KILL will not work otherwise. */
- stop_all_lwps ();
+ stop_all_lwps (0, NULL);
find_inferior (&all_threads, linux_kill_one_lwp, &pid);
@@ -790,7 +790,7 @@ linux_kill (int pid)
/* Since we presently can only stop all lwps of all processes, we
need to unstop lwps of other processes. */
- unstop_all_lwps (NULL);
+ unstop_all_lwps (0, NULL);
return 0;
}
@@ -840,7 +840,7 @@ linux_detach (int pid)
the thread is stopped to sucessfully detach. Second, thread_db
may need to uninstall thread event breakpoints from memory, which
only works with a stopped process anyway. */
- stop_all_lwps ();
+ stop_all_lwps (0, NULL);
#ifdef USE_THREAD_DB
thread_db_detach (process);
@@ -852,7 +852,7 @@ linux_detach (int pid)
/* Since we presently can only stop all lwps of all processes, we
need to unstop lwps of other processes. */
- unstop_all_lwps (NULL);
+ unstop_all_lwps (0, NULL);
return 0;
}
@@ -928,7 +928,7 @@ status_pending_p_callback (struct inferior_list_entry *entry, void *arg)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
ptid_t ptid = * (ptid_t *) arg;
- struct thread_info *thread = get_lwp_thread (lwp);
+ struct thread_info *thread;
/* Check if we're only interested in events from a specific process
or its lwps. */
@@ -941,7 +941,7 @@ status_pending_p_callback (struct inferior_list_entry *entry, void *arg)
/* 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_STOPPED)
+ && thread->last_status.kind != TARGET_WAITKIND_IGNORE)
return 0;
return lwp->status_pending_p;
@@ -1113,6 +1113,12 @@ handle_tracepoints (struct lwp_info *lwp)
struct thread_info *tinfo = get_lwp_thread (lwp);
int tpoint_related_event = 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++;
+
/* 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
@@ -1125,6 +1131,10 @@ handle_tracepoints (struct lwp_info *lwp)
actions. */
tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
+ lwp->suspended--;
+
+ gdb_assert (lwp->suspended == 0);
+
if (tpoint_related_event)
{
if (debug_threads)
@@ -1290,7 +1300,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
/* Cancel the step-over operation --- the thread that
started it is gone. */
if (finish_step_over (event_child))
- unstop_all_lwps (event_child);
+ unstop_all_lwps (1, event_child);
delete_lwp (event_child);
return lwpid;
}
@@ -1484,6 +1494,12 @@ cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data)
return 0;
}
+static void
+linux_cancel_breakpoints (void)
+{
+ find_inferior (&all_lwps, cancel_breakpoints_callback, NULL);
+}
+
/* Select one LWP out of those that have events pending. */
static void
@@ -1551,6 +1567,32 @@ gdb_wants_lwp_stopped (struct inferior_list_entry *entry)
thread->last_resume_kind = resume_stop;
}
+/* Decrement the suspend count of an LWP. */
+
+static int
+unsuspend_one_lwp (struct inferior_list_entry *entry, void *except)
+{
+ struct lwp_info *lwp = (struct lwp_info *) entry;
+
+ /* Ignore EXCEPT. */
+ if (lwp == except)
+ return 0;
+
+ lwp->suspended--;
+
+ gdb_assert (lwp->suspended >= 0);
+ return 0;
+}
+
+/* Decrement the suspend count of all LWPs, except EXCEPT, if non
+ NULL. */
+
+static void
+unsuspend_all_lwps (struct lwp_info *except)
+{
+ find_inferior (&all_lwps, unsuspend_one_lwp, except);
+}
+
/* Set all LWP's states as "want-stopped". */
static void
@@ -1806,12 +1848,17 @@ retry:
(*the_low_target.set_pc) (regcache, event_child->stop_pc);
}
- /* We've finished stepping over a breakpoint. We've stopped all
- LWPs momentarily except the stepping one. This is where we
- resume them all again. We're going to keep waiting, so use
- proceed, which handles stepping over the next breakpoint. */
+ /* We may have finished stepping over a breakpoint. If so,
+ we've stopped and suspended all LWPs momentarily except the
+ stepping one. This is where we resume them all again. We're
+ going to keep waiting, so use proceed, which handles stepping
+ over the next breakpoint. */
if (debug_threads)
fprintf (stderr, "proceeding all threads.\n");
+
+ if (step_over_finished)
+ unsuspend_all_lwps (event_child);
+
proceed_all_lwps ();
goto retry;
}
@@ -1833,7 +1880,7 @@ retry:
if (!non_stop)
{
/* In all-stop, stop all threads. */
- stop_all_lwps ();
+ 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
@@ -1863,7 +1910,7 @@ retry:
threads stopped by now anyway. In non-stop, we need to
re-resume threads that GDB wanted to be running. */
if (step_over_finished)
- unstop_all_lwps (event_child);
+ unstop_all_lwps (1, event_child);
}
ourstatus->kind = TARGET_WAITKIND_STOPPED;
@@ -1912,7 +1959,8 @@ retry:
ourstatus->kind,
ourstatus->value.sig);
- get_lwp_thread (event_child)->last_status = *ourstatus;
+ current_inferior->last_status = *ourstatus;
+
return ptid_of (event_child);
}
@@ -2021,15 +2069,37 @@ send_sigstop (struct lwp_info *lwp)
kill_lwp (pid, SIGSTOP);
}
-static void
-send_sigstop_callback (struct inferior_list_entry *entry)
+static int
+send_sigstop_callback (struct inferior_list_entry *entry, void *except)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
+ /* Ignore EXCEPT. */
+ if (lwp == except)
+ return 0;
+
if (lwp->stopped)
- return;
+ return 0;
send_sigstop (lwp);
+ return 0;
+}
+
+/* Increment the suspend count of an LWP, and stop it, if not stopped
+ yet. */
+static int
+suspend_and_send_sigstop_callback (struct inferior_list_entry *entry,
+ void *except)
+{
+ struct lwp_info *lwp = (struct lwp_info *) entry;
+
+ /* Ignore EXCEPT. */
+ if (lwp == except)
+ return 0;
+
+ lwp->suspended++;
+
+ return send_sigstop_callback (entry, except);
}
static void
@@ -2140,11 +2210,19 @@ wait_for_sigstop (struct inferior_list_entry *entry)
}
}
+/* Stop all lwps that aren't stopped yet, except EXCEPT, if not NULL.
+ If SUSPEND, then also increase the suspend count of every LWP,
+ except EXCEPT. */
+
static void
-stop_all_lwps (void)
+stop_all_lwps (int suspend, struct lwp_info *except)
{
stopping_threads = 1;
- for_each_inferior (&all_lwps, send_sigstop_callback);
+
+ if (suspend)
+ find_inferior (&all_lwps, suspend_and_send_sigstop_callback, except);
+ else
+ find_inferior (&all_lwps, send_sigstop_callback, except);
for_each_inferior (&all_lwps, wait_for_sigstop);
stopping_threads = 0;
}
@@ -2227,6 +2305,9 @@ linux_resume_one_lwp (struct lwp_info *lwp,
{
if (step == 0)
fprintf (stderr, "BAD - reinserting but not stepping.\n");
+ if (lwp->suspended)
+ fprintf (stderr, "BAD - reinserting and suspended(%d).\n",
+ lwp->suspended);
step = 1;
}
@@ -2423,6 +2504,17 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
return 0;
}
+ gdb_assert (lwp->suspended >= 0);
+
+ if (lwp->suspended)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Need step over [LWP %ld]? Ignoring, suspended\n",
+ lwpid_of (lwp));
+ return 0;
+ }
+
if (!lwp->need_step_over)
{
if (debug_threads)
@@ -2536,7 +2628,8 @@ start_step_over (struct lwp_info *lwp)
"Starting step-over on LWP %ld. Stopping all threads\n",
lwpid_of (lwp));
- stop_all_lwps ();
+ stop_all_lwps (1, lwp);
+ gdb_assert (lwp->suspended == 0);
if (debug_threads)
fprintf (stderr, "Done stopping all threads for step-over.\n");
@@ -2785,14 +2878,15 @@ linux_resume (struct thread_resume *resume_info, size_t n)
breakpoint that needs stepping over, we start a step-over operation
on that particular thread, and leave all others stopped. */
-static void
-proceed_one_lwp (struct inferior_list_entry *entry)
+static int
+proceed_one_lwp (struct inferior_list_entry *entry, void *except)
{
- struct lwp_info *lwp;
+ struct lwp_info *lwp = (struct lwp_info *) entry;
struct thread_info *thread;
int step;
- lwp = (struct lwp_info *) entry;
+ if (lwp == except)
+ return 0;
if (debug_threads)
fprintf (stderr,
@@ -2802,7 +2896,7 @@ proceed_one_lwp (struct inferior_list_entry *entry)
{
if (debug_threads)
fprintf (stderr, " LWP %ld already running\n", lwpid_of (lwp));
- return;
+ return 0;
}
thread = get_lwp_thread (lwp);
@@ -2813,7 +2907,7 @@ proceed_one_lwp (struct inferior_list_entry *entry)
if (debug_threads)
fprintf (stderr, " client wants LWP to remain %ld stopped\n",
lwpid_of (lwp));
- return;
+ return 0;
}
if (lwp->status_pending_p)
@@ -2821,14 +2915,16 @@ proceed_one_lwp (struct inferior_list_entry *entry)
if (debug_threads)
fprintf (stderr, " LWP %ld has pending status, leaving stopped\n",
lwpid_of (lwp));
- return;
+ return 0;
}
+ gdb_assert (lwp->suspended >= 0);
+
if (lwp->suspended)
{
if (debug_threads)
fprintf (stderr, " LWP %ld is suspended\n", lwpid_of (lwp));
- return;
+ return 0;
}
if (thread->last_resume_kind == resume_stop)
@@ -2854,6 +2950,21 @@ proceed_one_lwp (struct inferior_list_entry *entry)
step = thread->last_resume_kind == resume_step;
linux_resume_one_lwp (lwp, step, 0, NULL);
+ return 0;
+}
+
+static int
+unsuspend_and_proceed_one_lwp (struct inferior_list_entry *entry, void *except)
+{
+ struct lwp_info *lwp = (struct lwp_info *) entry;
+
+ if (lwp == except)
+ return 0;
+
+ lwp->suspended--;
+ gdb_assert (lwp->suspended >= 0);
+
+ return proceed_one_lwp (entry, except);
}
/* When we finish a step-over, set threads running again. If there's
@@ -2891,7 +3002,7 @@ proceed_all_lwps (void)
if (debug_threads)
fprintf (stderr, "Proceeding, no step-over needed\n");
- for_each_inferior (&all_lwps, proceed_one_lwp);
+ find_inferior (&all_lwps, proceed_one_lwp, NULL);
}
/* Stopped LWPs that the client wanted to be running, that don't have
@@ -2899,7 +3010,7 @@ proceed_all_lwps (void)
NULL. This undoes a stop_all_lwps call. */
static void
-unstop_all_lwps (struct lwp_info *except)
+unstop_all_lwps (int unsuspend, struct lwp_info *except)
{
if (debug_threads)
{
@@ -2911,14 +3022,10 @@ unstop_all_lwps (struct lwp_info *except)
"unstopping all lwps\n");
}
- /* Make sure proceed_one_lwp doesn't try to resume this thread. */
- if (except != NULL)
- ++except->suspended;
-
- for_each_inferior (&all_lwps, proceed_one_lwp);
-
- if (except != NULL)
- --except->suspended;
+ if (unsuspend)
+ find_inferior (&all_lwps, unsuspend_and_proceed_one_lwp, except);
+ else
+ find_inferior (&all_lwps, proceed_one_lwp, except);
}
#ifdef HAVE_LINUX_USRREGS
@@ -4296,9 +4403,18 @@ linux_thread_stopped (struct thread_info *thread)
/* This exposes stop-all-threads functionality to other modules. */
static void
-linux_pause_all (void)
+linux_pause_all (int freeze)
{
- stop_all_lwps ();
+ stop_all_lwps (freeze, NULL);
+}
+
+/* This exposes unstop-all-threads functionality to other gdbserver
+ modules. */
+
+static void
+linux_unpause_all (int unfreeze)
+{
+ unstop_all_lwps (unfreeze, NULL);
}
static struct target_ops linux_target_ops = {
@@ -4351,8 +4467,10 @@ static struct target_ops linux_target_ops = {
linux_read_pc,
linux_write_pc,
linux_thread_stopped,
+ NULL,
linux_pause_all,
- NULL, /* get_tib_address (Windows OS specific). */
+ linux_unpause_all,
+ linux_cancel_breakpoints
};
static void