+2014-01-08 Pedro Alves <palves@redhat.com>
+
+ * remote.c (remote_add_thread): Add threads silently if starting
+ up.
+ (remote_notice_new_inferior): If in all-stop, and starting up,
+ don't call notice_new_inferior.
+ (get_current_thread): New function, factored out from ...
+ (add_current_inferior_and_thread): ... this. Adjust.
+ (remote_start_remote) <all-stop>: Fetch the thread list. If we
+ found any thread, then select the remote's current thread as GDB's
+ current thread too.
+
2014-01-08 Joel Brobecker <brobecker@adacore.com>
* NEWS: Create a new section for the next release branch.
+2014-01-08 Pedro Alves <palves@redhat.com>
+
+ * gdbthread.h (struct thread_info) <status_pending_p>: New field.
+ * server.c (visit_actioned_threads, handle_pending_status): New
+ function.
+ (handle_v_cont): Factor out parts to ...
+ (resume): ... this new function. If in all-stop, and a thread
+ being resumed has a pending status, report it without actually
+ resuming.
+ (myresume): Adjust to use the new 'resume' function.
+ (clear_pending_status_callback, set_pending_status_callback)
+ (find_status_pending_thread_callback): New functions.
+ (handle_status): Handle the case of multiple threads having
+ interesting statuses to report. Report threads' real last signal
+ instead of always reporting GDB_SIGNAL_TRAP. Look for a thread
+ with an interesting thread to report the status for, instead of
+ always reporting the status of the first thread.
+
2014-01-01 Joel Brobecker <brobecker@adacore.com>
* gdbserver.c (gdbserver_version): Set copyright year to 2014.
/* The last wait status reported for this thread. */
struct target_waitstatus last_status;
+ /* True if LAST_STATUS hasn't been reported to GDB yet. */
+ int status_pending_p;
+
/* Given `while-stepping', a thread may be collecting data for more
than one tracepoint simultaneously. E.g.:
}
static void gdb_wants_all_threads_stopped (void);
+static void resume (struct thread_resume *actions, size_t n);
+
+/* Call CALLBACK for any thread to which ACTIONS applies to. Returns
+ true if CALLBACK returns true. Returns false if no matching thread
+ is found or CALLBACK results false. */
+
+static int
+visit_actioned_threads (const struct thread_resume *actions,
+ size_t num_actions,
+ int (*callback) (const struct thread_resume *,
+ struct thread_info *))
+{
+ struct inferior_list_entry *entry;
+
+ for (entry = all_threads.head; entry != NULL; entry = entry->next)
+ {
+ size_t i;
+
+ for (i = 0; i < num_actions; i++)
+ {
+ const struct thread_resume *action = &actions[i];
+
+ if (ptid_equal (action->thread, minus_one_ptid)
+ || ptid_equal (action->thread, entry->id)
+ || ((ptid_get_pid (action->thread)
+ == ptid_get_pid (entry->id))
+ && ptid_get_lwp (action->thread) == -1))
+ {
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ if ((*callback) (action, thread))
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Callback for visit_actioned_threads. If the thread has a pending
+ status to report, report it now. */
+
+static int
+handle_pending_status (const struct thread_resume *resumption,
+ struct thread_info *thread)
+{
+ if (thread->status_pending_p)
+ {
+ thread->status_pending_p = 0;
+
+ last_status = thread->last_status;
+ last_ptid = thread->entry.id;
+ prepare_resume_reply (own_buf, last_ptid, &last_status);
+ return 1;
+ }
+ return 0;
+}
/* Parse vCont packets. */
void
cont_thread = minus_one_ptid;
set_desired_inferior (0);
+ resume (resume_info, n);
+ free (resume_info);
+ return;
+
+err:
+ write_enn (own_buf);
+ free (resume_info);
+ return;
+}
+
+/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */
+
+static void
+resume (struct thread_resume *actions, size_t num_actions)
+{
if (!non_stop)
- enable_async_io ();
+ {
+ /* Check if among the threads that GDB wants actioned, there's
+ one with a pending status to report. If so, skip actually
+ resuming/stopping and report the pending event
+ immediately. */
+ if (visit_actioned_threads (actions, num_actions, handle_pending_status))
+ return;
- (*the_target->resume) (resume_info, n);
+ enable_async_io ();
+ }
- free (resume_info);
+ (*the_target->resume) (actions, num_actions);
if (non_stop)
write_ok (own_buf);
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
}
- return;
-
-err:
- write_enn (own_buf);
- free (resume_info);
- return;
}
/* Attach to a new program. Return 1 if successful, 0 if failure. */
n++;
}
- if (!non_stop)
- enable_async_io ();
-
- (*the_target->resume) (resume_info, n);
-
- if (non_stop)
- write_ok (own_buf);
- else
- {
- last_ptid = mywait (minus_one_ptid, &last_status, 0, 1);
-
- if (last_status.kind != TARGET_WAITKIND_EXITED
- && last_status.kind != TARGET_WAITKIND_SIGNALLED)
- {
- current_inferior->last_resume_kind = resume_stop;
- current_inferior->last_status = last_status;
- }
-
- prepare_resume_reply (own_buf, last_ptid, &last_status);
- disable_async_io ();
-
- if (last_status.kind == TARGET_WAITKIND_EXITED
- || last_status.kind == TARGET_WAITKIND_SIGNALLED)
- mourn_inferior (find_process_pid (ptid_get_pid (last_ptid)));
- }
+ resume (resume_info, n);
}
/* Callback for for_each_inferior. Make a new stop reply for each
process->gdb_detached = 0;
}
+/* Callback for for_each_inferior. Clear the thread's pending status
+ flag. */
+
+static void
+clear_pending_status_callback (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ thread->status_pending_p = 0;
+}
+
+/* Callback for for_each_inferior. If the thread is stopped with an
+ interesting event, mark it as having a pending event. */
+
+static void
+set_pending_status_callback (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
+ || (thread->last_status.value.sig != GDB_SIGNAL_0
+ /* A breakpoint, watchpoint or finished step from a previous
+ GDB run isn't considered interesting for a new GDB run.
+ If we left those pending, the new GDB could consider them
+ random SIGTRAPs. This leaves out real async traps. We'd
+ have to peek into the (target-specific) siginfo to
+ distinguish those. */
+ && thread->last_status.value.sig != GDB_SIGNAL_TRAP))
+ thread->status_pending_p = 1;
+}
+
+/* Callback for find_inferior. Return true if ENTRY (a thread) has a
+ pending status to report to GDB. */
+
+static int
+find_status_pending_thread_callback (struct inferior_list_entry *entry, void *data)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+
+ return thread->status_pending_p;
+}
+
/* Status handler for the '?' packet. */
static void
/* GDB is connected, don't forward events to the target anymore. */
for_each_inferior (&all_processes, gdb_reattached_process);
+ discard_queued_stop_replies (-1);
+ for_each_inferior (&all_threads, clear_pending_status_callback);
+
/* In non-stop mode, we must send a stop reply for each stopped
thread. In all-stop mode, just send one for the first stopped
thread we find. */
if (non_stop)
{
- discard_queued_stop_replies (-1);
find_inferior (&all_threads, queue_stop_reply_callback, NULL);
/* The first is sent immediatly. OK is sent if there is no
}
else
{
+ struct inferior_list_entry *thread = NULL;
+
pause_all (0);
stabilize_threads ();
gdb_wants_all_threads_stopped ();
- if (all_threads.head)
- {
- struct target_waitstatus status;
+ /* We can only report one status, but we might be coming out of
+ non-stop -- if more than one thread is stopped with
+ interesting events, leave events for the threads we're not
+ reporting now pending. They'll be reported the next time the
+ threads are resumed. Start by marking all interesting events
+ as pending. */
+ for_each_inferior (&all_threads, set_pending_status_callback);
+
+ /* Prefer the last thread that reported an event to GDB (even if
+ that was a GDB_SIGNAL_TRAP). */
+ if (last_status.kind != TARGET_WAITKIND_IGNORE
+ && last_status.kind != TARGET_WAITKIND_EXITED
+ && last_status.kind != TARGET_WAITKIND_SIGNALLED)
+ thread = find_inferior_id (&all_threads, last_ptid);
+
+ /* If the last event thread is not found for some reason, look
+ for some other thread that might have an event to report. */
+ if (thread == NULL)
+ thread = find_inferior (&all_threads,
+ find_status_pending_thread_callback, NULL);
+
+ /* If we're still out of luck, simply pick the first thread in
+ the thread list. */
+ if (thread == NULL)
+ thread = all_threads.head;
+
+ if (thread != NULL)
+ {
+ struct thread_info *tp = (struct thread_info *) thread;
+
+ /* We're reporting this event, so it's no longer
+ pending. */
+ tp->status_pending_p = 0;
+
+ /* GDB assumes the current thread is the thread we're
+ reporting the status for. */
+ general_thread = thread->id;
+ set_desired_inferior (1);
- status.kind = TARGET_WAITKIND_STOPPED;
- status.value.sig = GDB_SIGNAL_TRAP;
- prepare_resume_reply (own_buf,
- all_threads.head->id, &status);
+ gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE);
+ prepare_resume_reply (own_buf, tp->entry.id, &tp->last_status);
}
else
strcpy (own_buf, "W00");
static void
remote_add_thread (ptid_t ptid, int running)
{
- add_thread (ptid);
+ struct remote_state *rs = get_remote_state ();
+
+ /* GDB historically didn't pull threads in the initial connection
+ setup. If the remote target doesn't even have a concept of
+ threads (e.g., a bare-metal target), even if internally we
+ consider that a single-threaded target, mentioning a new thread
+ might be confusing to the user. Be silent then, preserving the
+ age old behavior. */
+ if (rs->starting_up)
+ add_thread_silent (ptid);
+ else
+ add_thread (ptid);
set_executing (ptid, running);
set_running (ptid, running);
/* If we found a new inferior, let the common code do whatever
it needs to with it (e.g., read shared libraries, insert
- breakpoints). */
+ breakpoints), unless we're just setting up an all-stop
+ connection. */
if (inf != NULL)
- notice_new_inferior (currthread, running, 0);
+ {
+ struct remote_state *rs = get_remote_state ();
+
+ if (non_stop || !rs->starting_up)
+ notice_new_inferior (currthread, running, 0);
+ }
}
}
return null_ptid;
}
+/* Determine the remote side's current thread. If we have a stop
+ reply handy (in WAIT_STATUS), maybe it's a T stop reply with a
+ "thread" register we can extract the current thread from. If not,
+ ask the remote which is the current thread with qC. The former
+ method avoids a roundtrip. */
+
+static ptid_t
+get_current_thread (char *wait_status)
+{
+ ptid_t ptid;
+
+ /* Note we don't use remote_parse_stop_reply as that makes use of
+ the target architecture, which we haven't yet fully determined at
+ this point. */
+ if (wait_status != NULL)
+ ptid = stop_reply_extract_thread (wait_status);
+ if (ptid_equal (ptid, null_ptid))
+ ptid = remote_current_thread (inferior_ptid);
+
+ return ptid;
+}
+
/* Query the remote target for which is the current thread/process,
add it to our tables, and update INFERIOR_PTID. The caller is
responsible for setting the state such that the remote end is ready
inferior_ptid = null_ptid;
- /* Now, if we have thread information, update inferior_ptid. First
- if we have a stop reply handy, maybe it's a T stop reply with a
- "thread" register we can extract the current thread from. If
- not, ask the remote which is the current thread, with qC. The
- former method avoids a roundtrip. Note we don't use
- remote_parse_stop_reply as that makes use of the target
- architecture, which we haven't yet fully determined at this
- point. */
- if (wait_status != NULL)
- ptid = stop_reply_extract_thread (wait_status);
- if (ptid_equal (ptid, null_ptid))
- ptid = remote_current_thread (inferior_ptid);
+ /* Now, if we have thread information, update inferior_ptid. */
+ ptid = get_current_thread (wait_status);
if (!ptid_equal (ptid, null_ptid))
{
strcpy (wait_status, rs->buf);
}
+ /* Fetch thread list. */
+ target_find_new_threads ();
+
/* Let the stub know that we want it to return the thread. */
set_continue_thread (minus_one_ptid);
- add_current_inferior_and_thread (wait_status);
+ if (thread_count () == 0)
+ {
+ /* Target has no concept of threads at all. GDB treats
+ non-threaded target as single-threaded; add a main
+ thread. */
+ add_current_inferior_and_thread (wait_status);
+ }
+ else
+ {
+ /* We have thread information; select the thread the target
+ says should be current. If we're reconnecting to a
+ multi-threaded program, this will ideally be the thread
+ that last reported an event before GDB disconnected. */
+ inferior_ptid = get_current_thread (wait_status);
+ if (ptid_equal (inferior_ptid, null_ptid))
+ {
+ /* Odd... The target was able to list threads, but not
+ tell us which thread was current (no "thread"
+ register in T stop reply?). Just pick the first
+ thread in the thread list then. */
+ inferior_ptid = thread_list->ptid;
+ }
+ }
/* init_wait_for_inferior should be called before get_offsets in order
to manage `inserted' flag in bp loc in a correct state.
+2014-01-08 Pedro Alves <palves@redhat.com>
+
+ * gdb.threads/reconnect-signal.c: New file.
+ * gdb.threads/reconnect-signal.exp: New file.
+
2014-01-07 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/source-dir.exp: New file.
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013-2014 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+
+static pthread_t thread_2;
+sig_atomic_t unlocked;
+
+/* The test has three threads, and it's always thread 2 that gets the
+ signal, to avoid spurious passes in case the remote side happens to
+ always pick the first or the last thread in the list as the
+ current/status thread on reconnection. */
+
+static void *
+start2 (void *arg)
+{
+ unsigned int count;
+
+ pthread_kill (thread_2, SIGUSR1);
+
+ for (count = 1; !unlocked && count != 0; count++)
+ usleep (1);
+ return NULL;
+}
+
+static void *
+start (void *arg)
+{
+ pthread_t thread;
+
+ pthread_create (&thread, NULL, start2, NULL);
+ pthread_join (thread, NULL);
+ return NULL;
+}
+
+void
+handle (int sig)
+{
+ unlocked = 1;
+}
+
+int
+main ()
+{
+ signal (SIGUSR1, handle);
+
+ pthread_create (&thread_2, NULL, start, NULL);
+ pthread_join (thread_2, NULL);
+
+ return 0;
+}
--- /dev/null
+# Copyright 2013-2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+# Test that disconnecting and reconnecting doesn't lose signals.
+
+set gdbserver_reconnect_p 1
+if { [info proc gdb_reconnect] == "" } {
+ return 0
+}
+
+standard_testfile
+set executable ${testfile}
+
+if { [gdb_compile_pthreads \
+ "${srcdir}/${subdir}/${srcfile}" \
+ "${binfile}" \
+ executable {debug}] != "" } {
+ untested "Couldn't compile test program."
+ return -1
+}
+
+clean_restart $executable
+
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+gdb_test "continue" "signal SIGUSR1.*" "continue to signal"
+
+# Check that it's thread 2 that is selected.
+gdb_test "info threads" "\\* 2 .*" "thread 2 is selected"
+
+set msg "save \$pc after signal"
+set saved_pc ""
+gdb_test_multiple "print/x \$pc" $msg {
+ -re "\\\$$decimal = (\[^\r\n\]*)\r\n$gdb_prompt $" {
+ set saved_pc $expect_out(1,string)
+ pass $msg
+ }
+}
+
+# Switch to the other thread.
+gdb_test "thread 1" "thread 1.*" "switch to thread 1"
+
+# Force GDB to select thread 1 on the remote end as well.
+gdb_test "print/x \$pc"
+
+gdb_test "disconnect" "Ending remote debugging\\." "disconnect after signal"
+
+set test "reconnect after signal"
+
+set res [gdb_reconnect]
+if { [lindex $res 0] == 0 } {
+ pass $test
+} else {
+ fail $test
+ return 0
+}
+
+# Check that thread 2 is re-selected.
+gdb_test "info threads" "\\* 2 .*" "thread 2 is selected on reconnect"
+
+# Check that the program is still alive, and stopped in the same spot.
+gdb_test "print/x \$pc" "\\\$$decimal = $saved_pc" "check \$pc after signal"
+
+# Check that we didn't lose the signal.
+gdb_test "info program" "stopped with signal SIGUSR1,.*"
+
+# Nor does the program.
+gdb_test "b handle" "Breakpoint .*" "set breakpoint in signal handler"
+gdb_test "continue" "handle.*" "continue to signal handler"