diff options
-rw-r--r-- | gdb/ChangeLog | 11 | ||||
-rw-r--r-- | gdb/linux-nat.c | 28 | ||||
-rw-r--r-- | gdb/linux-nat.h | 4 | ||||
-rw-r--r-- | gdb/linux-thread-db.c | 51 |
4 files changed, 90 insertions, 4 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7b234b9..854070b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,16 @@ 2009-05-18 Pedro Alves <pedro@codesourcery.com> + * linux-nat.h (linux_proc_get_tgid): Declare. + * linux-nat.c (linux_proc_get_tgid): New. + * linux-thread-db.c (struct thread_db_info): New field + `need_stale_parent_threads_check'. + (add_thread_db_info): Set it. + (find_new_threads_callback): Ignore stale fork parent threads. + (thread_db_resume): New. + (init_thread_db_ops): Install thread_db_resume. + +2009-05-18 Pedro Alves <pedro@codesourcery.com> + * fork-child.c (fork_inferior): Only reset the thread list if this is the first inferior. (startup_inferior): If the target support multi-process, tell it diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 553d676..c3aa94f 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1129,6 +1129,34 @@ exit_lwp (struct lwp_info *lp) delete_lwp (lp->ptid); } +/* Return an lwp's tgid, found in `/proc/PID/status'. */ + +int +linux_proc_get_tgid (int lwpid) +{ + FILE *status_file; + char buf[100]; + int tgid = -1; + + snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid); + status_file = fopen (buf, "r"); + if (status_file != NULL) + { + while (fgets (buf, sizeof (buf), status_file)) + { + if (strncmp (buf, "Tgid:", 5) == 0) + { + tgid = strtoul (buf + strlen ("Tgid:"), NULL, 10); + break; + } + } + + fclose (status_file); + } + + return tgid; +} + /* Detect `T (stopped)' in `/proc/PID/status'. Other states including `T (tracing stop)' are reported as false. */ diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 5c17425..d1ed6fc 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -99,6 +99,10 @@ int thread_db_attach_lwp (ptid_t ptid); /* Find process PID's pending signal set from /proc/pid/status. */ void linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigset_t *ignored); +/* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not + found. */ +extern int linux_proc_get_tgid (int lwpid); + /* linux-nat functions for handling fork events. */ extern void linux_enable_event_reporting (ptid_t ptid); diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 6e68965..3efcae8 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -104,6 +104,13 @@ struct thread_db_info /* Connection to the libthread_db library. */ td_thragent_t *thread_agent; + /* True if we need to apply the workaround for glibc/BZ5983. When + we catch a PTRACE_O_TRACEFORK, and go query the child's thread + list, nptl_db returns the parent's threads in addition to the new + (single) child thread. If this flag is set, we do extra work to + be able to ignore such stale entries. */ + int need_stale_parent_threads_check; + /* Location of the thread creation event breakpoint. The code at this location in the child process will be called by the pthread library whenever a new thread is created. By setting a special @@ -168,6 +175,7 @@ add_thread_db_info (void *handle) info = xcalloc (1, sizeof (*info)); info->pid = ptid_get_pid (inferior_ptid); info->handle = handle; + info->need_stale_parent_threads_check = 1; info->next = thread_db_list; thread_db_list = info; @@ -1269,8 +1277,6 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data) if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) return 0; /* A zombie -- ignore. */ - ptid = ptid_build (info->pid, ti.ti_lid, 0); - if (ti.ti_tid == 0) { /* A thread ID of zero means that this is the main thread, but @@ -1279,14 +1285,29 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data) be yet. Just enable event reporting and otherwise ignore it. */ + /* In that case, we're not stopped in a fork syscall and don't + need this glibc bug workaround. */ + info->need_stale_parent_threads_check = 0; + err = info->td_thr_event_enable_p (th_p, 1); if (err != TD_OK) - error (_("Cannot enable thread event reporting for %s: %s"), - target_pid_to_str (ptid), thread_db_err_str (err)); + error (_("Cannot enable thread event reporting for LWP %d: %s"), + (int) ti.ti_lid, thread_db_err_str (err)); return 0; } + /* Ignore stale parent threads, caused by glibc/BZ5983. This is a + bit expensive, as it needs to open /proc/pid/status, so try to + avoid doing the work if we know we don't have to. */ + if (info->need_stale_parent_threads_check) + { + int tgid = linux_proc_get_tgid (ti.ti_lid); + if (tgid != -1 && tgid != info->pid) + return 0; + } + + ptid = ptid_build (info->pid, ti.ti_lid, 0); tp = find_thread_pid (ptid); if (tp == NULL || tp->private == NULL) attach_thread (ptid, th_p, &ti); @@ -1480,6 +1501,27 @@ thread_db_get_ada_task_ptid (long lwp, long thread) } static void +thread_db_resume (struct target_ops *ops, + ptid_t ptid, int step, enum target_signal signo) +{ + struct target_ops *beneath = find_target_beneath (ops); + struct thread_db_info *info; + + if (ptid_equal (ptid, minus_one_ptid)) + info = get_thread_db_info (GET_PID (inferior_ptid)); + else + info = get_thread_db_info (GET_PID (ptid)); + + /* This workaround is only needed for child fork lwps stopped in a + PTRACE_O_TRACEFORK event. When the inferior is resumed, the + workaround can be disabled. */ + if (info) + info->need_stale_parent_threads_check = 0; + + beneath->to_resume (beneath, ptid, step, signo); +} + +static void init_thread_db_ops (void) { thread_db_ops.to_shortname = "multi-thread"; @@ -1487,6 +1529,7 @@ init_thread_db_ops (void) thread_db_ops.to_doc = "Threads and pthreads support."; thread_db_ops.to_detach = thread_db_detach; thread_db_ops.to_wait = thread_db_wait; + thread_db_ops.to_resume = thread_db_resume; thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior; thread_db_ops.to_find_new_threads = thread_db_find_new_threads; thread_db_ops.to_pid_to_str = thread_db_pid_to_str; |