2014-04-25 Pedro Alves <palves@redhat.com>
+ PR server/16255
+ * common/linux-ptrace.c (linux_ptrace_attach_warnings): Rename to ...
+ (linux_ptrace_attach_fail_reason): ... this. Remove "warning: "
+ and newline from built string.
+ * common/linux-ptrace.h (linux_ptrace_attach_warnings): Rename to ...
+ (linux_ptrace_attach_fail_reason): ... this.
+ * linux-nat.c (linux_nat_attach): Adjust to use
+ linux_ptrace_attach_fail_reason.
+
+2014-04-25 Pedro Alves <palves@redhat.com>
+
* remote.c (struct remote_state): Remove multi_process_aware,
non_stop_aware, cond_tracepoints, cond_breakpoints,
breakpoint_commands, fast_tracepoints, static_tracepoints,
there are no supported features. */
static int current_ptrace_options = -1;
-/* Find all possible reasons we could fail to attach PID and append these
- newline terminated reason strings to initialized BUFFER. '\0' termination
- of BUFFER must be done by the caller. */
+/* Find all possible reasons we could fail to attach PID and append
+ these as strings to the already initialized BUFFER. '\0'
+ termination of BUFFER must be done by the caller. */
void
-linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer)
+linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer)
{
pid_t tracerpid;
tracerpid = linux_proc_get_tracerpid (pid);
if (tracerpid > 0)
- buffer_xml_printf (buffer, _("warning: process %d is already traced "
- "by process %d\n"),
+ buffer_xml_printf (buffer, _("process %d is already traced "
+ "by process %d"),
(int) pid, (int) tracerpid);
if (linux_proc_pid_is_zombie (pid))
- buffer_xml_printf (buffer, _("warning: process %d is a zombie "
- "- the process has already terminated\n"),
+ buffer_xml_printf (buffer, _("process %d is a zombie "
+ "- the process has already terminated"),
(int) pid);
}
#define __WALL 0x40000000 /* Wait for any child. */
#endif
-extern void linux_ptrace_attach_warnings (pid_t pid, struct buffer *buffer);
+extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
extern void linux_ptrace_init_warnings (void);
extern void linux_enable_event_reporting (pid_t pid);
extern int linux_supports_tracefork (void);
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. */
-static void
-linux_attach_lwp_1 (unsigned long lwpid, int initial)
+int
+linux_attach_lwp (ptid_t ptid)
{
- ptid_t ptid;
struct lwp_info *new_lwp;
+ int lwpid = ptid_get_lwp (ptid);
if (ptrace (PTRACE_ATTACH, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0)
!= 0)
- {
- struct buffer buffer;
-
- if (!initial)
- {
- /* If we fail to attach to an LWP, just warn. */
- fprintf (stderr, "Cannot attach to lwp %ld: %s (%d)\n", lwpid,
- strerror (errno), errno);
- fflush (stderr);
- return;
- }
-
- /* If we fail to attach to a process, report an error. */
- buffer_init (&buffer);
- linux_ptrace_attach_warnings (lwpid, &buffer);
- buffer_grow_str0 (&buffer, "");
- error ("%sCannot attach to lwp %ld: %s (%d)", buffer_finish (&buffer),
- lwpid, strerror (errno), errno);
- }
-
- if (initial)
- /* If lwp is the tgid, we handle adding existing threads later.
- Otherwise we just add lwp without bothering about any other
- threads. */
- ptid = ptid_build (lwpid, lwpid, 0);
- else
- {
- /* Note that extracting the pid from the current inferior is
- safe, since we're always called in the context of the same
- process as this new thread. */
- int pid = pid_of (current_inferior);
- ptid = ptid_build (pid, lwpid, 0);
- }
+ return errno;
new_lwp = add_lwp (ptid);
end of the list, and so the new thread has not yet reached
wait_for_sigstop (but will). */
new_lwp->stop_expected = 1;
-}
-void
-linux_attach_lwp (unsigned long lwpid)
-{
- linux_attach_lwp_1 (lwpid, 0);
+ return 0;
}
/* Attach to PID. If PID is the tgid, attach to it and all
static int
linux_attach (unsigned long pid)
{
+ ptid_t ptid = ptid_build (pid, pid, 0);
+ int err;
+
/* Attach to PID. We will check for other threads
soon. */
- linux_attach_lwp_1 (pid, 1);
+ 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)
{
/* At this point we attached to the tgid. Scan the task for
existing threads. */
- unsigned long lwp;
int new_threads_found;
int iterations = 0;
- struct dirent *dp;
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
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
- && find_thread_ptid (ptid_build (pid, lwp, 0)) == NULL)
+ if (lwp != 0 && find_thread_ptid (ptid) == NULL)
{
- linux_attach_lwp_1 (lwp, 0);
- new_threads_found++;
+ int err;
if (debug_threads)
- debug_printf ("Found and attached to new lwp %ld\n",
- lwp);
+ debug_printf ("Found new lwp %ld\n", lwp);
+
+ err = linux_attach_lwp (ptid);
+ if (err != 0)
+ warning ("Cannot attach to lwp %ld: %s",
+ lwp,
+ linux_attach_fail_reason_string (ptid, err));
+
+ new_threads_found++;
}
}
int linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine);
-void linux_attach_lwp (unsigned long pid);
+/* Attach to PTID. Returns 0 on success, non-zero otherwise (an
+ errno). */
+int linux_attach_lwp (ptid_t ptid);
+
+/* Return the reason an attach failed, in string form. ERR is the
+ error returned by linux_attach_lwp (an errno). This string should
+ be copied into a buffer by the client if the string will not be
+ immediately used, or if it must persist. */
+char *linux_attach_fail_reason_string (ptid_t ptid, int err);
+
struct lwp_info *find_lwp_pid (ptid_t ptid);
void linux_stop_lwp (struct lwp_info *lwp);
static int
attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
{
+ struct process_info *proc = current_process ();
+ int pid = pid_of (proc);
+ ptid_t ptid = ptid_build (pid, ti_p->ti_lid, 0);
struct lwp_info *lwp;
+ int err;
if (debug_threads)
debug_printf ("Attaching to thread %ld (LWP %d)\n",
ti_p->ti_tid, ti_p->ti_lid);
- linux_attach_lwp (ti_p->ti_lid);
- lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid));
- if (lwp == NULL)
+ err = linux_attach_lwp (ptid);
+ if (err != 0)
{
- warning ("Could not attach to thread %ld (LWP %d)\n",
- ti_p->ti_tid, ti_p->ti_lid);
+ warning ("Could not attach to thread %ld (LWP %d): %s\n",
+ ti_p->ti_tid, ti_p->ti_lid,
+ linux_attach_fail_reason_string (ptid, err));
return 0;
}
+ lwp = find_lwp_pid (ptid);
+ gdb_assert (lwp != NULL);
lwp->thread_known = 1;
lwp->th = *th_p;
if (thread_db_use_events)
{
td_err_e err;
- struct thread_db *thread_db = current_process ()->private->thread_db;
+ struct thread_db *thread_db = proc->private->thread_db;
err = thread_db->td_thr_event_enable_p (th_p, 1);
if (err != TD_OK)
make_cleanup (xfree, message);
buffer_init (&buffer);
- linux_ptrace_attach_warnings (pid, &buffer);
+ linux_ptrace_attach_fail_reason (pid, &buffer);
buffer_grow_str0 (&buffer, "");
buffer_s = buffer_finish (&buffer);
make_cleanup (xfree, buffer_s);
- throw_error (ex.error, "%s%s", buffer_s, message);
+ if (*buffer_s != '\0')
+ throw_error (ex.error, "warning: %s\n%s", buffer_s, message);
+ else
+ throw_error (ex.error, "%s", message);
}
/* The ptrace base target adds the main thread with (pid,0,0)
+2014-04-25 Simon Marchi <simon.marchi@ericsson.com>
+ Pedro Alves <palves@redhat.com>
+
+ PR server/16255
+ * gdb.multi/multi-attach.c: New file.
+ * gdb.multi/multi-attach.exp: New file.
+
2014-04-25 Pedro Alves <palves@redhat.com>
* gdb.base/cond-eval-mode.exp (warning): Move trailing \r\n to
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2007-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/>. */
+
+/* This program is intended to be started outside of gdb, and then
+ attached to by gdb. It loops for a while, but not forever. */
+
+#include <unistd.h>
+#include <pthread.h>
+
+static void *
+thread_func (void *arg)
+{
+ int i;
+
+ for (i = 0; i < 120; i++)
+ sleep (1);
+
+ return NULL;
+}
+
+int main ()
+{
+ int i;
+ pthread_t thread;
+
+ pthread_create (&thread, NULL, thread_func, NULL);
+
+ for (i = 0; i < 120; i++)
+ sleep (1);
+
+ pthread_join (thread, NULL);
+
+ return 0;
+}
--- /dev/null
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007-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 attaching to multiple threaded programs.
+
+standard_testfile
+
+# We need to use TCL's exec to get the pid.
+if [is_remote target] then {
+ return 0
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug additional_flags=-lpthread}]} {
+ return -1
+}
+
+# Start the programs running and then wait for a bit, to be sure that
+# they can be attached to.
+set testpid1 [eval exec $binfile &]
+set testpid2 [eval exec $binfile &]
+exec sleep 2
+if { [istarget "*-*-cygwin*"] } {
+ # testpid{1,2} are the Cygwin PID, GDB uses the Windows PID, which might be
+ # different due to the way fork/exec works.
+ set testpid1 [ exec ps -e | gawk "{ if (\$1 == $testpid1) print \$4; }" ]
+ set testpid2 [ exec ps -e | gawk "{ if (\$1 == $testpid2) print \$4; }" ]
+}
+
+gdb_test "attach $testpid1" \
+ "Attaching to program: .*, process $testpid1.*(in|at).*" \
+ "attach to program 1"
+gdb_test "backtrace" ".*main.*" "backtrace 1"
+
+gdb_test "add-inferior -exec $binfile" \
+ "Added inferior 2.*" \
+ "add second inferior"
+gdb_test "inferior 2" ".*Switching to inferior 2.*" "switch to second inferior"
+
+gdb_test "attach $testpid2" \
+ "Attaching to program: .*, process $testpid2.*(in|at).*" \
+ "attach to program 2"
+gdb_test "backtrace" ".*main.*" "backtrace 2"
+
+gdb_test "kill" "" "kill inferior 2" "Kill the program being debugged.*" "y"
+gdb_test "inferior 1" ".*Switching to inferior 1.*"
+gdb_test "kill" "" "kill inferior 1" "Kill the program being debugged.*" "y"