From 24a09b5f373c09e5cb56523e6fcb76e81bcd4784 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Tue, 23 Oct 2007 20:05:03 +0000 Subject: [PATCH] * inferiors.c (change_inferior_id): Delete. (add_pid_to_list, pull_pid_from_list): New. * linux-low.c (PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG) (PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) (PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXEC, PTRACE_O_TRACEVFORKDONE) (PTRACE_O_TRACEEXIT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK) (PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, PTRACE_EVENT_VFORK_DONE) (PTRACE_EVENT_EXIT, __WALL): Provide default definitions. (stopped_pids, thread_db_active, must_set_ptrace_flags): New variables. (using_threads): Always set to 1. (handle_extended_wait): New. (add_process): Do not set TID. (linux_create_inferior): Set must_set_ptrace_flags. (linux_attach_lwp): Remove TID argument. Do not check using_threads. Use PTRACE_SETOPTIONS. Call new_thread_notify. Update all callers. (linux_thread_alive): Rename TID argument to LWPID. (linux_wait_for_process): Handle unknown processes. Do not use TID. (linux_wait_for_event): Do not use TID or check using_threads. Update call to dead_thread_notify. Call handle_extended_wait. (linux_create_inferior): Use PTRACE_SETOPTIONS. (send_sigstop): Delete sigstop_sent. (wait_for_sigstop): Avoid TID. (linux_supports_tracefork_flag, linux_tracefork_child, my_waitpid) (linux_test_for_tracefork): New. (linux_lookup_signals): Use thread_db_active and linux_supports_tracefork_flag. (initialize_low): Use thread_db_active and linux_test_for_tracefork. * linux-low.h (get_process_thread): Avoid TID. (struct process_ifo): Move thread_known and tid to the end. Remove sigstop_sent. (linux_attach_lwp, thread_db_init): Update prototypes. * server.h (change_inferior_id): Delete prototype. (add_pid_to_list, pull_pid_from_list): New prototypes. * thread-db.c (thread_db_use_events): New. (find_first_thread): Rename to... (find_one_thread): ...this. Update callers and messages. Do not call fatal. Check thread_db_use_events. Do not call change_inferior_id or new_thread_notify. (maybe_attach_thread): Update. Do not call new_thread_notify. (thread_db_init): Set thread_db_use_events. Check use_events. * utils.c (fatal, warning): Correct message prefix. --- gdb/gdbserver/ChangeLog | 44 +++++++ gdb/gdbserver/inferiors.c | 44 ++++--- gdb/gdbserver/linux-low.c | 305 ++++++++++++++++++++++++++++++++++++++++------ gdb/gdbserver/linux-low.h | 17 ++- gdb/gdbserver/server.h | 4 +- gdb/gdbserver/thread-db.c | 80 ++++++------ gdb/gdbserver/utils.c | 4 +- 7 files changed, 392 insertions(+), 106 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index bafe109..229e7b1 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,47 @@ +2007-10-23 Daniel Jacobowitz + + * inferiors.c (change_inferior_id): Delete. + (add_pid_to_list, pull_pid_from_list): New. + * linux-low.c (PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG) + (PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) + (PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXEC, PTRACE_O_TRACEVFORKDONE) + (PTRACE_O_TRACEEXIT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK) + (PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, PTRACE_EVENT_VFORK_DONE) + (PTRACE_EVENT_EXIT, __WALL): Provide default definitions. + (stopped_pids, thread_db_active, must_set_ptrace_flags): New variables. + (using_threads): Always set to 1. + (handle_extended_wait): New. + (add_process): Do not set TID. + (linux_create_inferior): Set must_set_ptrace_flags. + (linux_attach_lwp): Remove TID argument. Do not check using_threads. + Use PTRACE_SETOPTIONS. Call new_thread_notify. Update all callers. + (linux_thread_alive): Rename TID argument to LWPID. + (linux_wait_for_process): Handle unknown processes. Do not use TID. + (linux_wait_for_event): Do not use TID or check using_threads. Update + call to dead_thread_notify. Call handle_extended_wait. + (linux_create_inferior): Use PTRACE_SETOPTIONS. + (send_sigstop): Delete sigstop_sent. + (wait_for_sigstop): Avoid TID. + (linux_supports_tracefork_flag, linux_tracefork_child, my_waitpid) + (linux_test_for_tracefork): New. + (linux_lookup_signals): Use thread_db_active and + linux_supports_tracefork_flag. + (initialize_low): Use thread_db_active and linux_test_for_tracefork. + * linux-low.h (get_process_thread): Avoid TID. + (struct process_ifo): Move thread_known and tid to the end. Remove + sigstop_sent. + (linux_attach_lwp, thread_db_init): Update prototypes. + * server.h (change_inferior_id): Delete prototype. + (add_pid_to_list, pull_pid_from_list): New prototypes. + * thread-db.c (thread_db_use_events): New. + (find_first_thread): Rename to... + (find_one_thread): ...this. Update callers and messages. Do not + call fatal. Check thread_db_use_events. Do not call + change_inferior_id or new_thread_notify. + (maybe_attach_thread): Update. Do not call new_thread_notify. + (thread_db_init): Set thread_db_use_events. Check use_events. + * utils.c (fatal, warning): Correct message prefix. + 2007-10-15 Daniel Jacobowitz * Makefile.in (clean): Remove new files. diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index 3b23767..f631e39 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -65,21 +65,6 @@ for_each_inferior (struct inferior_list *list, } } -/* When debugging a single-threaded program, the threads list (such as - it is) is indexed by PID. When debugging a multi-threaded program, - we index by TID. This ugly routine replaces the - first-debugged-thread's PID with its TID. */ - -void -change_inferior_id (struct inferior_list *list, - unsigned long new_id) -{ - if (list->head != list->tail) - error ("tried to change thread ID after multiple threads are created"); - - list->head->id = new_id; -} - void remove_inferior (struct inferior_list *list, struct inferior_list_entry *entry) @@ -318,3 +303,32 @@ clear_inferiors (void) clear_list (&all_threads); clear_list (&all_dlls); } + +/* Two utility functions for a truly degenerate inferior_list: a simple + PID listing. */ + +void +add_pid_to_list (struct inferior_list *list, unsigned long pid) +{ + struct inferior_list_entry *new_entry; + + new_entry = malloc (sizeof (struct inferior_list_entry)); + new_entry->id = pid; + add_inferior_to_list (list, new_entry); +} + +int +pull_pid_from_list (struct inferior_list *list, unsigned long pid) +{ + struct inferior_list_entry *new_entry; + + new_entry = find_inferior_id (list, pid); + if (new_entry == NULL) + return 0; + else + { + remove_inferior (list, new_entry); + free (new_entry); + return 1; + } +} diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index fa5c5b2..93577b9 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -44,26 +44,67 @@ #define O_LARGEFILE 0 #endif +/* If the system headers did not provide the constants, hard-code the normal + values. */ +#ifndef PTRACE_EVENT_FORK + +#define PTRACE_SETOPTIONS 0x4200 +#define PTRACE_GETEVENTMSG 0x4201 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 +#define PTRACE_O_TRACEFORK 0x00000002 +#define PTRACE_O_TRACEVFORK 0x00000004 +#define PTRACE_O_TRACECLONE 0x00000008 +#define PTRACE_O_TRACEEXEC 0x00000010 +#define PTRACE_O_TRACEVFORKDONE 0x00000020 +#define PTRACE_O_TRACEEXIT 0x00000040 + +/* Wait extended result codes for the above trace options. */ +#define PTRACE_EVENT_FORK 1 +#define PTRACE_EVENT_VFORK 2 +#define PTRACE_EVENT_CLONE 3 +#define PTRACE_EVENT_EXEC 4 +#define PTRACE_EVENT_VFORK_DONE 5 +#define PTRACE_EVENT_EXIT 6 + +#endif /* PTRACE_EVENT_FORK */ + +/* We can't always assume that this flag is available, but all systems + with the ptrace event handlers also have __WALL, so it's safe to use + in some contexts. */ +#ifndef __WALL +#define __WALL 0x40000000 /* Wait for any child. */ +#endif + #ifdef __UCLIBC__ #if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__)) #define HAS_NOMMU #endif #endif -/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead, - however. This requires changing the ID in place when we go from !using_threads - to using_threads, immediately. +/* ``all_threads'' is keyed by the LWP ID, which we use as the GDB protocol + representation of the thread ID. ``all_processes'' is keyed by the process ID - which on Linux is (presently) the same as the LWP ID. */ struct inferior_list all_processes; +/* A list of all unknown processes which receive stop signals. Some other + process will presumably claim each of these as forked children + momentarily. */ + +struct inferior_list stopped_pids; + /* FIXME this is a bit of a hack, and could be removed. */ int stopping_threads; /* FIXME make into a target method? */ -int using_threads; +int using_threads = 1; +static int thread_db_active; + +static int must_set_ptrace_flags; static void linux_resume_one_process (struct inferior_list_entry *entry, int step, int signal, siginfo_t *info); @@ -71,6 +112,7 @@ static void linux_resume (struct thread_resume *resume_info); static void stop_all_processes (void); static int linux_wait_for_event (struct thread_info *child); static int check_removed_breakpoint (struct process_info *event_child); +static void *add_process (unsigned long pid); struct pending_signals { @@ -91,6 +133,56 @@ static int use_regsets_p = 1; /* FIXME: Delete eventually. */ #define inferior_pid (pid_of (get_thread_process (current_inferior))) +static void +handle_extended_wait (struct process_info *event_child, int wstat) +{ + int event = wstat >> 16; + struct process_info *new_process; + + if (event == PTRACE_EVENT_CLONE) + { + unsigned long new_pid; + int ret, status; + + ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid); + + /* If we haven't already seen the new PID stop, wait for it now. */ + if (! pull_pid_from_list (&stopped_pids, new_pid)) + { + /* The new child has a pending SIGSTOP. We can't affect it until it + hits the SIGSTOP, but we're already attached. */ + + do { + ret = waitpid (new_pid, &status, __WALL); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) + perror_with_name ("waiting for new child"); + else if (ret != new_pid) + warning ("wait returned unexpected PID %d", ret); + else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP) + warning ("wait returned unexpected status 0x%x", status); + } + + ptrace (PTRACE_SETOPTIONS, new_pid, 0, PTRACE_O_TRACECLONE); + + new_process = (struct process_info *) add_process (new_pid); + add_thread (new_pid, new_process, new_pid); + new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); + + if (stopping_threads) + new_process->stopped = 1; + else + ptrace (PTRACE_CONT, new_pid, 0, 0); + + /* Always resume the current thread. If we are stopping + threads, it will have a pending SIGSTOP; we may as well + collect it now. */ + linux_resume_one_process (&event_child->head, + event_child->stepping, 0, NULL); + } +} + /* This function should only be called if the process got a SIGTRAP. The SIGTRAP could mean several things. @@ -133,9 +225,6 @@ add_process (unsigned long pid) memset (process, 0, sizeof (*process)); process->head.id = pid; - - /* Default to tid == lwpid == pid. */ - process->tid = pid; process->lwpid = pid; add_inferior_to_list (&all_processes, &process->head); @@ -180,6 +269,7 @@ linux_create_inferior (char *program, char **allargs) new_process = add_process (pid); add_thread (pid, new_process, pid); + must_set_ptrace_flags = 1; return pid; } @@ -187,7 +277,7 @@ linux_create_inferior (char *program, char **allargs) /* Attach to an inferior process. */ void -linux_attach_lwp (unsigned long pid, unsigned long tid) +linux_attach_lwp (unsigned long pid) { struct process_info *new_process; @@ -198,13 +288,16 @@ linux_attach_lwp (unsigned long pid, unsigned long tid) fflush (stderr); /* If we fail to attach to an LWP, just return. */ - if (!using_threads) + if (all_threads.head == NULL) _exit (0177); return; } + ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE); + new_process = (struct process_info *) add_process (pid); - add_thread (tid, new_process, pid); + add_thread (pid, new_process, pid); + new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH brings it to a halt. We should ignore that SIGSTOP and resume the process @@ -225,7 +318,7 @@ linux_attach (unsigned long pid) { struct process_info *process; - linux_attach_lwp (pid, pid); + linux_attach_lwp (pid); /* Don't ignore the initial SIGSTOP if we just attached to this process. It will be collected by wait shortly. */ @@ -338,9 +431,9 @@ linux_join (void) /* Return nonzero if the given thread is still alive. */ static int -linux_thread_alive (unsigned long tid) +linux_thread_alive (unsigned long lwpid) { - if (find_inferior_id (&all_threads, tid) != NULL) + if (find_inferior_id (&all_threads, lwpid) != NULL) return 1; else return 0; @@ -440,6 +533,7 @@ linux_wait_for_process (struct process_info **childp, int *wstatp) if (*childp != NULL) to_wait_for = (*childp)->lwpid; +retry: while (1) { ret = waitpid (to_wait_for, wstatp, WNOHANG); @@ -474,6 +568,18 @@ linux_wait_for_process (struct process_info **childp, int *wstatp) if (to_wait_for == -1) *childp = (struct process_info *) find_inferior_id (&all_processes, ret); + /* If we didn't find a process, one of two things presumably happened: + - A process we started and then detached from has exited. Ignore it. + - A process we are controlling has forked and the new child's stop + was reported to us by the kernel. Save its PID. */ + if (*childp == NULL && WIFSTOPPED (*wstatp)) + { + add_pid_to_list (&stopped_pids, ret); + goto retry; + } + else if (*childp == NULL) + goto retry; + (*childp)->stopped = 1; (*childp)->pending_is_breakpoint = 0; @@ -483,7 +589,7 @@ linux_wait_for_process (struct process_info **childp, int *wstatp) && WIFSTOPPED (*wstatp)) { current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, (*childp)->tid); + find_inferior_id (&all_threads, (*childp)->lwpid); /* For testing only; i386_stop_pc prints out a diagnostic. */ if (the_low_target.get_pc != NULL) get_stop_pc (); @@ -548,20 +654,19 @@ linux_wait_for_event (struct thread_info *child) error ("event from unknown child"); current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, event_child->tid); + find_inferior_id (&all_threads, event_child->lwpid); /* Check for thread exit. */ - if (using_threads && ! WIFSTOPPED (wstat)) + if (! WIFSTOPPED (wstat)) { if (debug_threads) - fprintf (stderr, "Thread %ld (LWP %ld) exiting\n", - event_child->tid, event_child->head.id); + fprintf (stderr, "LWP %ld exiting\n", event_child->head.id); /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return wstat; - dead_thread_notify (event_child->tid); + dead_thread_notify (thread_id_to_gdb_id (event_child->lwpid)); remove_inferior (&all_processes, &event_child->head); free (event_child); @@ -577,8 +682,7 @@ linux_wait_for_event (struct thread_info *child) continue; } - if (using_threads - && WIFSTOPPED (wstat) + if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP && event_child->stop_expected) { @@ -590,6 +694,13 @@ linux_wait_for_event (struct thread_info *child) continue; } + if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP + && wstat >> 16 != 0) + { + handle_extended_wait (event_child, wstat); + continue; + } + /* If GDB is not interested in this signal, don't stop other threads, and don't report it to GDB. Just resume the inferior right away. We do this for threading-related @@ -601,18 +712,20 @@ linux_wait_for_event (struct thread_info *child) thread library? */ if (WIFSTOPPED (wstat) && !event_child->stepping - && ((using_threads && (WSTOPSIG (wstat) == __SIGRTMIN - || WSTOPSIG (wstat) == __SIGRTMIN + 1)) - || (pass_signals[target_signal_from_host (WSTOPSIG (wstat))] - && (WSTOPSIG (wstat) != SIGSTOP - || !event_child->sigstop_sent)))) + && ( +#ifdef USE_THREAD_DB + (thread_db_active && (WSTOPSIG (wstat) == __SIGRTMIN + || WSTOPSIG (wstat) == __SIGRTMIN + 1)) + || +#endif + (pass_signals[target_signal_from_host (WSTOPSIG (wstat))] + && (WSTOPSIG (wstat) != SIGSTOP || !stopping_threads)))) { siginfo_t info, *info_p; if (debug_threads) - fprintf (stderr, "Ignored signal %d for %ld (LWP %ld).\n", - WSTOPSIG (wstat), event_child->tid, - event_child->head.id); + fprintf (stderr, "Ignored signal %d for LWP %ld.\n", + WSTOPSIG (wstat), event_child->head.id); if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0) info_p = &info; @@ -769,6 +882,12 @@ retry: stop_all_processes (); disable_async_io (); + if (must_set_ptrace_flags) + { + ptrace (PTRACE_SETOPTIONS, inferior_pid, 0, PTRACE_O_TRACECLONE); + must_set_ptrace_flags = 0; + } + /* If we are waiting for a particular child, and it exited, linux_wait_for_event will return its exit status. Similarly if the last child exited. If this is not the last child, however, @@ -863,7 +982,6 @@ send_sigstop (struct inferior_list_entry *entry) fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id); kill_lwp (process->head.id, SIGSTOP); - process->sigstop_sent = 1; } static void @@ -880,7 +998,7 @@ wait_for_sigstop (struct inferior_list_entry *entry) saved_inferior = current_inferior; saved_tid = ((struct inferior_list_entry *) saved_inferior)->id; thread = (struct thread_info *) find_inferior_id (&all_threads, - process->tid); + process->lwpid); wstat = linux_wait_for_event (thread); /* If we stopped with a non-SIGSTOP signal, save it for later @@ -890,9 +1008,8 @@ wait_for_sigstop (struct inferior_list_entry *entry) && WSTOPSIG (wstat) != SIGSTOP) { if (debug_threads) - fprintf (stderr, "Process %ld (thread %ld) " - "stopped with non-sigstop status %06x\n", - process->lwpid, process->tid, wstat); + fprintf (stderr, "LWP %ld stopped with non-sigstop status %06x\n", + process->lwpid, wstat); process->status_pending_p = 1; process->status_pending = wstat; process->stop_expected = 1; @@ -1593,14 +1710,127 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) return 0; } +static int linux_supports_tracefork_flag; + +/* A helper function for linux_test_for_tracefork, called after fork (). */ + +static void +linux_tracefork_child (void) +{ + ptrace (PTRACE_TRACEME, 0, 0, 0); + kill (getpid (), SIGSTOP); + fork (); + _exit (0); +} + +/* Wrapper function for waitpid which handles EINTR. */ + +static int +my_waitpid (int pid, int *status, int flags) +{ + int ret; + do + { + ret = waitpid (pid, status, flags); + } + while (ret == -1 && errno == EINTR); + + return ret; +} + +/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. Make + sure that we can enable the option, and that it had the desired + effect. */ + +static void +linux_test_for_tracefork (void) +{ + int child_pid, ret, status; + long second_pid; + + linux_supports_tracefork_flag = 0; + + child_pid = fork (); + if (child_pid == -1) + perror_with_name ("fork"); + + if (child_pid == 0) + linux_tracefork_child (); + + ret = my_waitpid (child_pid, &status, 0); + if (ret == -1) + perror_with_name ("waitpid"); + else if (ret != child_pid) + error ("linux_test_for_tracefork: waitpid: unexpected result %d.", ret); + if (! WIFSTOPPED (status)) + error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status); + + ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK); + if (ret != 0) + { + ret = ptrace (PTRACE_KILL, child_pid, 0, 0); + if (ret != 0) + { + warning ("linux_test_for_tracefork: failed to kill child"); + return; + } + + ret = my_waitpid (child_pid, &status, 0); + if (ret != child_pid) + warning ("linux_test_for_tracefork: failed to wait for killed child"); + else if (!WIFSIGNALED (status)) + warning ("linux_test_for_tracefork: unexpected wait status 0x%x from " + "killed child", status); + + return; + } + + ret = ptrace (PTRACE_CONT, child_pid, 0, 0); + if (ret != 0) + warning ("linux_test_for_tracefork: failed to resume child"); + + ret = my_waitpid (child_pid, &status, 0); + + if (ret == child_pid && WIFSTOPPED (status) + && status >> 16 == PTRACE_EVENT_FORK) + { + second_pid = 0; + ret = ptrace (PTRACE_GETEVENTMSG, child_pid, 0, &second_pid); + if (ret == 0 && second_pid != 0) + { + int second_status; + + linux_supports_tracefork_flag = 1; + my_waitpid (second_pid, &second_status, 0); + ret = ptrace (PTRACE_KILL, second_pid, 0, 0); + if (ret != 0) + warning ("linux_test_for_tracefork: failed to kill second child"); + my_waitpid (second_pid, &status, 0); + } + } + else + warning ("linux_test_for_tracefork: unexpected result from waitpid " + "(%d, status 0x%x)", ret, status); + + do + { + ret = ptrace (PTRACE_KILL, child_pid, 0, 0); + if (ret != 0) + warning ("linux_test_for_tracefork: failed to kill child"); + my_waitpid (child_pid, &status, 0); + } + while (WIFSTOPPED (status)); +} + + static void linux_look_up_symbols (void) { #ifdef USE_THREAD_DB - if (using_threads) + if (thread_db_active) return; - using_threads = thread_db_init (); + thread_db_active = thread_db_init (!linux_supports_tracefork_flag); #endif } @@ -1782,10 +2012,11 @@ linux_init_signals () void initialize_low (void) { - using_threads = 0; + thread_db_active = 0; set_target_ops (&linux_target_ops); set_breakpoint_data (the_low_target.breakpoint, the_low_target.breakpoint_len); init_registers (); linux_init_signals (); + linux_test_for_tracefork (); } diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index c94716f..cdf16aa 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -81,14 +81,12 @@ extern struct linux_target_ops the_low_target; #define get_thread_process(thr) (get_process (inferior_target_data (thr))) #define get_process_thread(proc) ((struct thread_info *) \ find_inferior_id (&all_threads, \ - get_process (proc)->tid)) + get_process (proc)->lwpid)) struct process_info { struct inferior_list_entry head; - int thread_known; unsigned long lwpid; - unsigned long tid; /* If this flag is set, the next SIGSTOP will be ignored (the process will be immediately resumed). This means that either we @@ -105,10 +103,6 @@ struct process_info /* When stopped is set, the last wait status recorded for this process. */ int last_status; - /* If this flag is set, we have sent a SIGSTOP to this process and are - waiting for it to stop. */ - int sigstop_sent; - /* If this flag is set, STATUS_PENDING is a waitstatus that has not yet been reported. */ int status_pending_p; @@ -135,16 +129,19 @@ struct process_info struct thread_resume *resume; + int thread_known; + unsigned long tid; #ifdef HAVE_THREAD_DB_H - /* The thread handle, used for e.g. TLS access. */ + /* The thread handle, used for e.g. TLS access. Only valid if + THREAD_KNOWN is set. */ td_thrhandle_t th; #endif }; extern struct inferior_list all_processes; -void linux_attach_lwp (unsigned long pid, unsigned long tid); +void linux_attach_lwp (unsigned long pid); -int thread_db_init (void); +int thread_db_init (int use_events); int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, CORE_ADDR load_module, CORE_ADDR *address); diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index b032f42..d350f44 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -137,8 +137,8 @@ void *inferior_target_data (struct thread_info *); void set_inferior_target_data (struct thread_info *, void *); void *inferior_regcache_data (struct thread_info *); void set_inferior_regcache_data (struct thread_info *, void *); -void change_inferior_id (struct inferior_list *list, - unsigned long new_id); +void add_pid_to_list (struct inferior_list *list, unsigned long pid); +int pull_pid_from_list (struct inferior_list *list, unsigned long pid); void loaded_dll (const char *name, CORE_ADDR base_addr); void unloaded_dll (const char *name, CORE_ADDR base_addr); diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index adcb946..1869dbd 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -24,6 +24,8 @@ extern int debug_threads; +static int thread_db_use_events; + #ifdef HAVE_THREAD_DB_H #include #endif @@ -39,7 +41,7 @@ static struct ps_prochandle proc_handle; /* Connection to the libthread_db library. */ static td_thragent_t *thread_agent; -static int find_first_thread (void); +static int find_one_thread (int); static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); static char * @@ -152,7 +154,7 @@ thread_db_create_event (CORE_ADDR where) created threads. */ process = get_thread_process (current_inferior); if (process->thread_known == 0) - find_first_thread (); + find_one_thread (process->lwpid); /* msg.event == TD_EVENT_CREATE */ @@ -224,7 +226,7 @@ thread_db_enable_reporting () } static int -find_first_thread (void) +find_one_thread (int lwpid) { td_thrhandle_t th; td_thrinfo_t ti; @@ -232,54 +234,50 @@ find_first_thread (void) struct thread_info *inferior; struct process_info *process; - inferior = (struct thread_info *) all_threads.head; + inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid); process = get_thread_process (inferior); if (process->thread_known) return 1; - /* Get information about the one thread we know we have. */ + /* Get information about this thread. */ err = td_ta_map_lwp2thr (thread_agent, process->lwpid, &th); if (err != TD_OK) - error ("Cannot get first thread handle: %s", thread_db_err_str (err)); + error ("Cannot get thread handle for LWP %d: %s", + lwpid, thread_db_err_str (err)); err = td_thr_get_info (&th, &ti); if (err != TD_OK) - error ("Cannot get first thread info: %s", thread_db_err_str (err)); + error ("Cannot get thread info for LWP %d: %s", + lwpid, thread_db_err_str (err)); if (debug_threads) - fprintf (stderr, "Found first thread %ld (LWP %d)\n", + fprintf (stderr, "Found thread %ld (LWP %d)\n", ti.ti_tid, ti.ti_lid); if (process->lwpid != ti.ti_lid) - fatal ("PID mismatch! Expected %ld, got %ld", - (long) process->lwpid, (long) ti.ti_lid); + { + warning ("PID mismatch! Expected %ld, got %ld", + (long) process->lwpid, (long) ti.ti_lid); + return 0; + } - /* If the new thread ID is zero, a final thread ID will be available - later. Do not enable thread debugging yet. */ - if (ti.ti_tid == 0) + if (thread_db_use_events) { err = td_thr_event_enable (&th, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti.ti_lid, thread_db_err_str (err)); - return 0; } - /* Switch to indexing the threads list by TID. */ - change_inferior_id (&all_threads, ti.ti_tid); - - new_thread_notify (ti.ti_tid); + /* If the new thread ID is zero, a final thread ID will be available + later. Do not enable thread debugging yet. */ + if (ti.ti_tid == 0) + return 0; - process->tid = ti.ti_tid; - process->lwpid = ti.ti_lid; process->thread_known = 1; + process->tid = ti.ti_tid; process->th = th; - err = td_thr_event_enable (&th, 1); - if (err != TD_OK) - error ("Cannot enable thread event reporting for %d: %s", - ti.ti_lid, thread_db_err_str (err)); - return 1; } @@ -291,16 +289,16 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) struct process_info *process; inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_tid); + ti_p->ti_lid); if (inferior != NULL) return; if (debug_threads) fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); - linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid); + linux_attach_lwp (ti_p->ti_lid); inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_tid); + ti_p->ti_lid); if (inferior == NULL) { warning ("Could not attach to thread %ld (LWP %d)\n", @@ -310,17 +308,17 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) process = inferior_target_data (inferior); - new_thread_notify (ti_p->ti_tid); - process->tid = ti_p->ti_tid; - process->lwpid = ti_p->ti_lid; - process->thread_known = 1; process->th = *th_p; - err = td_thr_event_enable (th_p, 1); - if (err != TD_OK) - error ("Cannot enable thread event reporting for %d: %s", - ti_p->ti_lid, thread_db_err_str (err)); + + if (thread_db_use_events) + { + err = td_thr_event_enable (th_p, 1); + if (err != TD_OK) + error ("Cannot enable thread event reporting for %d: %s", + ti_p->ti_lid, thread_db_err_str (err)); + } } static int @@ -350,7 +348,7 @@ thread_db_find_new_threads (void) /* This function is only called when we first initialize thread_db. First locate the initial thread. If it is not ready for debugging yet, then stop. */ - if (find_first_thread () == 0) + if (find_one_thread (all_threads.head->id) == 0) return; /* Iterate over all user-space threads to discover new threads. */ @@ -387,7 +385,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, process = get_thread_process (thread); if (!process->thread_known) - find_first_thread (); + find_one_thread (process->lwpid); if (!process->thread_known) return TD_NOTHR; @@ -409,7 +407,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, } int -thread_db_init () +thread_db_init (int use_events) { int err; @@ -428,6 +426,8 @@ thread_db_init () /* Allow new symbol lookups. */ all_symbols_looked_up = 0; + thread_db_use_events = use_events; + err = td_ta_new (&proc_handle, &thread_agent); switch (err) { @@ -438,7 +438,7 @@ thread_db_init () case TD_OK: /* The thread library was detected. */ - if (thread_db_enable_reporting () == 0) + if (use_events && thread_db_enable_reporting () == 0) return 0; thread_db_find_new_threads (); thread_db_look_up_symbols (); diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index 32b843b..b754aa6 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -78,7 +78,7 @@ fatal (const char *string,...) { va_list args; va_start (args, string); - fprintf (stderr, "gdb: "); + fprintf (stderr, "gdbserver: "); vfprintf (stderr, string, args); fprintf (stderr, "\n"); va_end (args); @@ -91,7 +91,7 @@ warning (const char *string,...) { va_list args; va_start (args, string); - fprintf (stderr, "gdb: "); + fprintf (stderr, "gdbserver: "); vfprintf (stderr, string, args); fprintf (stderr, "\n"); va_end (args); -- 2.7.4