aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog40
-rw-r--r--gdb/gdbserver/ChangeLog10
-rw-r--r--gdb/gdbserver/linux-low.c152
-rw-r--r--gdb/gdbserver/linux-low.h6
-rw-r--r--gdb/gdbserver/thread-db.c2
-rw-r--r--gdb/linux-nat.c93
-rw-r--r--gdb/linux-nat.h4
-rw-r--r--gdb/nat/linux-procfs.c153
-rw-r--r--gdb/nat/linux-procfs.h32
-rw-r--r--gdb/nat/linux-ptrace.c33
-rw-r--r--gdb/nat/linux-ptrace.h8
11 files changed, 406 insertions, 127 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6eb4926..4ee17b3 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,45 @@
2015-01-09 Pedro Alves <palves@redhat.com>
+ * linux-nat.c (attach_proc_task_lwp_callback): New function.
+ (linux_nat_attach): Use linux_proc_attach_tgid_threads.
+ (wait_lwp, linux_nat_filter_event): If not set yet, set the lwp's
+ ptrace option flags.
+ * linux-nat.h (struct lwp_info) <must_set_ptrace_flags>: New
+ field.
+ * nat/linux-procfs.c: Include <dirent.h>.
+ (linux_proc_get_int): New parameter "warn". Handle it.
+ (linux_proc_get_tgid): Adjust.
+ (linux_proc_get_tracerpid): Rename to ...
+ (linux_proc_get_tracerpid_nowarn): ... this.
+ (linux_proc_pid_get_state): New function, factored out from
+ (linux_proc_pid_has_state): ... this. Add new parameter "warn"
+ and handle it.
+ (linux_proc_pid_is_gone): New function.
+ (linux_proc_pid_is_stopped): Adjust.
+ (linux_proc_pid_is_zombie_maybe_warn)
+ (linux_proc_pid_is_zombie_nowarn): New functions.
+ (linux_proc_pid_is_zombie): Use
+ linux_proc_pid_is_zombie_maybe_warn.
+ (linux_proc_attach_tgid_threads): New function.
+ * nat/linux-procfs.h (linux_proc_get_tgid): Update comment.
+ (linux_proc_get_tracerpid): Rename to ...
+ (linux_proc_get_tracerpid_nowarn): ... this, and update comment.
+ (linux_proc_pid_is_gone): New declaration.
+ (linux_proc_pid_is_zombie): Update comment.
+ (linux_proc_pid_is_zombie_nowarn): New declaration.
+ (linux_proc_attach_lwp_func): New typedef.
+ (linux_proc_attach_tgid_threads): New declaration.
+ * nat/linux-ptrace.c (linux_ptrace_attach_fail_reason): Adjust to
+ use nowarn functions.
+ (linux_ptrace_attach_fail_reason_string): Move here from
+ gdbserver/linux-low.c and rename.
+ (ptrace_supports_feature): If the current ptrace options are not
+ known yet, check them now, instead of asserting.
+ * nat/linux-ptrace.h (linux_ptrace_attach_fail_reason_string):
+ Declare.
+
+2015-01-09 Pedro Alves <palves@redhat.com>
+
* linux-thread-db.c (thread_db_find_new_threads_silently)
(try_thread_db_load_1, try_thread_db_load, thread_db_load_search)
(find_new_threads_once): Print debug output on gdb_stdlog.
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 6937ec0..2ee6a00 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,13 @@
+2015-01-09 Pedro Alves <palves@redhat.com>
+
+ * linux-low.c (linux_attach_fail_reason_string): Move to
+ nat/linux-ptrace.c, and rename.
+ (linux_attach_lwp): Update comment.
+ (attach_proc_task_lwp_callback): New function.
+ (linux_attach): Adjust to rename and use
+ linux_proc_attach_tgid_threads.
+ (linux_attach_fail_reason_string): Delete declaration.
+
2015-01-01 Joel Brobecker <brobecker@adacore.com>
* gdbreplay.c (gdbreplay_version): Update copyright year to 2015.
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 0d85189..268ee5c 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -631,31 +631,8 @@ linux_create_inferior (char *program, char **allargs)
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)
@@ -739,6 +716,50 @@ 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;
+}
+
/* Attach to PID. If PID is the tgid, attach to it and all
of its threads. */
@@ -753,7 +774,7 @@ linux_attach (unsigned long pid)
err = linux_attach_lwp (ptid);
if (err != 0)
error ("Cannot attach to process %ld: %s",
- pid, linux_attach_fail_reason_string (ptid, err));
+ pid, linux_ptrace_attach_fail_reason_string (ptid, err));
linux_add_process (pid, 1);
@@ -767,75 +788,16 @@ linux_attach (unsigned long pid)
thread->last_resume_kind = resume_stop;
}
- if (linux_proc_get_tgid (pid) == pid)
- {
- DIR *dir;
- char pathname[128];
-
- sprintf (pathname, "/proc/%ld/task", pid);
-
- dir = opendir (pathname);
-
- if (!dir)
- {
- fprintf (stderr, "Could not open /proc/%ld/task.\n", pid);
- fflush (stderr);
- }
- 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);
-
- 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++;
- }
- }
-
- if (!new_threads_found)
- iterations++;
- else
- iterations = 0;
-
- rewinddir (dir);
- }
- closedir (dir);
- }
- }
-
+ /* 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);
return 0;
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 1a1d69c..97b163f 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -351,12 +351,6 @@ int linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine);
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);
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index 71bc984..4e0d32a 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -339,7 +339,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
{
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));
+ linux_ptrace_attach_fail_reason_string (ptid, err));
return 0;
}
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 73fd2cb..0adf3a9 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1137,6 +1137,73 @@ linux_nat_create_inferior (struct target_ops *ops,
#endif /* HAVE_PERSONALITY */
}
+/* 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)
+{
+ struct lwp_info *lp;
+
+ /* Ignore LWPs we're already attached to. */
+ lp = find_lwp_pid (ptid);
+ if (lp == NULL)
+ {
+ int lwpid = ptid_get_lwp (ptid);
+
+ if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) < 0)
+ {
+ int err = errno;
+
+ /* 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_linux_nat)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Cannot attach to lwp %d: "
+ "thread is gone (%d: %s)\n",
+ lwpid, err, safe_strerror (err));
+ }
+ }
+ else
+ {
+ warning (_("Cannot attach to lwp %d: %s\n"),
+ lwpid,
+ linux_ptrace_attach_fail_reason_string (ptid,
+ err));
+ }
+ }
+ else
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "PTRACE_ATTACH %s, 0, 0 (OK)\n",
+ target_pid_to_str (ptid));
+
+ lp = add_lwp (ptid);
+ lp->cloned = 1;
+
+ /* The next time we wait for this LWP we'll see a SIGSTOP as
+ PTRACE_ATTACH brings it to a halt. */
+ lp->signalled = 1;
+
+ /* We need to wait for a stop before being able to make the
+ next ptrace call on this LWP. */
+ lp->must_set_ptrace_flags = 1;
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
static void
linux_nat_attach (struct target_ops *ops, const char *args, int from_tty)
{
@@ -1230,6 +1297,16 @@ linux_nat_attach (struct target_ops *ops, const char *args, int from_tty)
lp->status = status;
+ /* We must attach to every LWP. If /proc is mounted, use that to
+ find them now. The inferior may be using raw clone instead of
+ using pthreads. But even if it is using pthreads, 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 (ptid_get_pid (lp->ptid),
+ attach_proc_task_lwp_callback);
+
if (target_can_async_p ())
target_async (inferior_event_handler, 0);
}
@@ -2159,6 +2236,14 @@ wait_lwp (struct lwp_info *lp)
gdb_assert (WIFSTOPPED (status));
lp->stopped = 1;
+ if (lp->must_set_ptrace_flags)
+ {
+ struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ lp->must_set_ptrace_flags = 0;
+ }
+
/* Handle GNU/Linux's syscall SIGTRAPs. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SYSCALL_SIGTRAP)
{
@@ -2783,6 +2868,14 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
ever being continued.) */
lp->stopped = 1;
+ if (WIFSTOPPED (status) && lp->must_set_ptrace_flags)
+ {
+ struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ lp->must_set_ptrace_flags = 0;
+ }
+
/* Handle GNU/Linux's syscall SIGTRAPs. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SYSCALL_SIGTRAP)
{
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index b65b2fe..8a44324 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -33,6 +33,10 @@ struct lwp_info
and overall process id. */
ptid_t ptid;
+ /* If this flag is set, we need to set the event request flags the
+ next time we see this LWP stop. */
+ int must_set_ptrace_flags;
+
/* Non-zero if this LWP is cloned. In this context "cloned" means
that the LWP is reporting to its parent using a signal other than
SIGCHLD. */
diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index f020d27..00b6a70 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -19,12 +19,13 @@
#include "common-defs.h"
#include "linux-procfs.h"
#include "filestuff.h"
+#include <dirent.h>
/* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not
found. */
static int
-linux_proc_get_int (pid_t lwpid, const char *field)
+linux_proc_get_int (pid_t lwpid, const char *field, int warn)
{
size_t field_len = strlen (field);
FILE *status_file;
@@ -35,7 +36,8 @@ linux_proc_get_int (pid_t lwpid, const char *field)
status_file = gdb_fopen_cloexec (buf, "r");
if (status_file == NULL)
{
- warning (_("unable to open /proc file '%s'"), buf);
+ if (warn)
+ warning (_("unable to open /proc file '%s'"), buf);
return -1;
}
@@ -56,45 +58,87 @@ linux_proc_get_int (pid_t lwpid, const char *field)
int
linux_proc_get_tgid (pid_t lwpid)
{
- return linux_proc_get_int (lwpid, "Tgid");
+ return linux_proc_get_int (lwpid, "Tgid", 1);
}
/* See linux-procfs.h. */
pid_t
-linux_proc_get_tracerpid (pid_t lwpid)
+linux_proc_get_tracerpid_nowarn (pid_t lwpid)
{
- return linux_proc_get_int (lwpid, "TracerPid");
+ return linux_proc_get_int (lwpid, "TracerPid", 0);
}
-/* Return non-zero if 'State' of /proc/PID/status contains STATE. */
+/* Fill in BUFFER, a buffer with BUFFER_SIZE bytes with the 'State'
+ line of /proc/PID/status. Returns -1 on failure to open the /proc
+ file, 1 if the line is found, and 0 if not found. If WARN, warn on
+ failure to open the /proc file. */
static int
-linux_proc_pid_has_state (pid_t pid, const char *state)
+linux_proc_pid_get_state (pid_t pid, char *buffer, size_t buffer_size,
+ int warn)
{
- char buffer[100];
FILE *procfile;
- int retval;
int have_state;
- xsnprintf (buffer, sizeof (buffer), "/proc/%d/status", (int) pid);
+ xsnprintf (buffer, buffer_size, "/proc/%d/status", (int) pid);
procfile = gdb_fopen_cloexec (buffer, "r");
if (procfile == NULL)
{
- warning (_("unable to open /proc file '%s'"), buffer);
- return 0;
+ if (warn)
+ warning (_("unable to open /proc file '%s'"), buffer);
+ return -1;
}
have_state = 0;
- while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+ while (fgets (buffer, buffer_size, procfile) != NULL)
if (strncmp (buffer, "State:", 6) == 0)
{
have_state = 1;
break;
}
- retval = (have_state && strstr (buffer, state) != NULL);
fclose (procfile);
- return retval;
+ return have_state;
+}
+
+/* See linux-procfs.h declaration. */
+
+int
+linux_proc_pid_is_gone (pid_t pid)
+{
+ char buffer[100];
+ int have_state;
+
+ have_state = linux_proc_pid_get_state (pid, buffer, sizeof buffer, 0);
+ if (have_state < 0)
+ {
+ /* If we can't open the status file, assume the thread has
+ disappeared. */
+ return 1;
+ }
+ else if (have_state == 0)
+ {
+ /* No "State:" line, assume thread is alive. */
+ return 0;
+ }
+ else
+ {
+ return (strstr (buffer, "Z (") != NULL
+ || strstr (buffer, "X (") != NULL);
+ }
+}
+
+/* Return non-zero if 'State' of /proc/PID/status contains STATE. If
+ WARN, warn on failure to open the /proc file. */
+
+static int
+linux_proc_pid_has_state (pid_t pid, const char *state, int warn)
+{
+ char buffer[100];
+ int have_state;
+
+ have_state = linux_proc_pid_get_state (pid, buffer, sizeof buffer, warn);
+ return (have_state > 0 && strstr (buffer, state) != NULL);
}
/* Detect `T (stopped)' in `/proc/PID/status'.
@@ -103,7 +147,24 @@ linux_proc_pid_has_state (pid_t pid, const char *state)
int
linux_proc_pid_is_stopped (pid_t pid)
{
- return linux_proc_pid_has_state (pid, "T (stopped)");
+ return linux_proc_pid_has_state (pid, "T (stopped)", 1);
+}
+
+/* Return non-zero if PID is a zombie. If WARN, warn on failure to
+ open the /proc file. */
+
+static int
+linux_proc_pid_is_zombie_maybe_warn (pid_t pid, int warn)
+{
+ return linux_proc_pid_has_state (pid, "Z (zombie)", warn);
+}
+
+/* See linux-procfs.h declaration. */
+
+int
+linux_proc_pid_is_zombie_nowarn (pid_t pid)
+{
+ return linux_proc_pid_is_zombie_maybe_warn (pid, 0);
}
/* See linux-procfs.h declaration. */
@@ -111,7 +172,7 @@ linux_proc_pid_is_stopped (pid_t pid)
int
linux_proc_pid_is_zombie (pid_t pid)
{
- return linux_proc_pid_has_state (pid, "Z (zombie)");
+ return linux_proc_pid_is_zombie_maybe_warn (pid, 1);
}
/* See linux-procfs.h declaration. */
@@ -132,3 +193,61 @@ linux_proc_pid_get_ns (pid_t pid, const char *ns)
return NULL;
}
+
+/* See linux-procfs.h. */
+
+void
+linux_proc_attach_tgid_threads (pid_t pid,
+ linux_proc_attach_lwp_func attach_lwp)
+{
+ DIR *dir;
+ char pathname[128];
+ int new_threads_found;
+ int iterations;
+
+ if (linux_proc_get_tgid (pid) != pid)
+ return;
+
+ xsnprintf (pathname, sizeof (pathname), "/proc/%ld/task", (long) pid);
+ dir = opendir (pathname);
+ if (dir == NULL)
+ {
+ warning (_("Could not open /proc/%ld/task.\n"), (long) pid);
+ return;
+ }
+
+ /* Scan the task list for existing 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. */
+ for (iterations = 0; iterations < 2; iterations++)
+ {
+ struct dirent *dp;
+
+ new_threads_found = 0;
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp;
+
+ /* Fetch one lwp. */
+ lwp = strtoul (dp->d_name, NULL, 10);
+ if (lwp != 0)
+ {
+ ptid_t ptid = ptid_build (pid, lwp, 0);
+
+ if (attach_lwp (ptid))
+ new_threads_found = 1;
+ }
+ }
+
+ if (new_threads_found)
+ {
+ /* Start over. */
+ iterations = -1;
+ }
+
+ rewinddir (dir);
+ }
+
+ closedir (dir);
+}
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index bc2fe2e..6f2a404 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -22,28 +22,50 @@
#include <unistd.h>
/* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not
- found. */
+ found. Failure to open the /proc file results in a warning. */
extern int linux_proc_get_tgid (pid_t lwpid);
-/* Return the TracerPid of LWPID from /proc/pid/status. Returns -1 if not
- found. */
+/* Return the TracerPid of LWPID from /proc/pid/status. Returns -1 if
+ not found. Does not warn on failure to open the /proc file. */
-extern pid_t linux_proc_get_tracerpid (pid_t lwpid);
+extern pid_t linux_proc_get_tracerpid_nowarn (pid_t lwpid);
/* Detect `T (stopped)' in `/proc/PID/status'.
Other states including `T (tracing stop)' are reported as false. */
extern int linux_proc_pid_is_stopped (pid_t pid);
-/* Return non-zero if PID is a zombie. */
+/* Return non-zero if PID is a zombie. Failure to open the
+ /proc/pid/status file results in a warning. */
extern int linux_proc_pid_is_zombie (pid_t pid);
+/* Return non-zero if PID is a zombie. Does not warn on failure to
+ open the /proc file. */
+
+extern int linux_proc_pid_is_zombie_nowarn (pid_t pid);
+
+/* Return non-zero if /proc/PID/status indicates that PID is gone
+ (i.e., in Z/Zombie or X/Dead state). Failure to open the /proc
+ file is assumed to indicate the thread is gone. */
+
+extern int linux_proc_pid_is_gone (pid_t pid);
+
/* Return an opaque string identifying PID's NS namespace or NULL if
* the information is unavailable. The returned string must be
* released with xfree. */
extern char *linux_proc_pid_get_ns (pid_t pid, const char *ns);
+/* Callback function for linux_proc_attach_tgid_threads. If the PTID
+ thread is not yet known, try to attach to it and return true,
+ otherwise return false. */
+typedef int (*linux_proc_attach_lwp_func) (ptid_t ptid);
+
+/* If PID is a tgid, scan the /proc/PID/task/ directory for existing
+ threads, and call FUNC for each thread found. */
+extern void linux_proc_attach_tgid_threads (pid_t pid,
+ linux_proc_attach_lwp_func func);
+
#endif /* COMMON_LINUX_PROCFS_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 9eac8ff..3981592 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -43,18 +43,44 @@ linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer)
{
pid_t tracerpid;
- tracerpid = linux_proc_get_tracerpid (pid);
+ tracerpid = linux_proc_get_tracerpid_nowarn (pid);
if (tracerpid > 0)
buffer_xml_printf (buffer, _("process %d is already traced "
"by process %d"),
(int) pid, (int) tracerpid);
- if (linux_proc_pid_is_zombie (pid))
+ if (linux_proc_pid_is_zombie_nowarn (pid))
buffer_xml_printf (buffer, _("process %d is a zombie "
"- the process has already terminated"),
(int) pid);
}
+/* See linux-ptrace.h. */
+
+char *
+linux_ptrace_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;
+}
+
#if defined __i386__ || defined __x86_64__
/* Address of the 'ret' instruction in asm code block below. */
@@ -508,7 +534,8 @@ linux_disable_event_reporting (pid_t pid)
static int
ptrace_supports_feature (int ptrace_options)
{
- gdb_assert (current_ptrace_options >= 0);
+ if (current_ptrace_options == -1)
+ linux_check_ptrace_features ();
return ((current_ptrace_options & ptrace_options) == ptrace_options);
}
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index b6fd7fc..137b61a 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -89,6 +89,14 @@ struct buffer;
#endif
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
+
+/* Find all possible reasons we could have failed to attach to PTID
+ and return them as a string. ERR is the error PTRACE_ATTACH failed
+ with (an errno). The result is stored in a static buffer. This
+ string should be copied into a buffer by the client if the string
+ will not be immediately used, or if it must persist. */
+extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
+
extern void linux_ptrace_init_warnings (void);
extern void linux_enable_event_reporting (pid_t pid, int attached);
extern void linux_disable_event_reporting (pid_t pid);