From ae13219ef81570640d856de602d606752f1b562a Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Mon, 2 Jul 2007 15:35:36 +0000 Subject: [PATCH] * inferiors.c (change_inferior_id): Add comment. * linux-low.c (check_removed_breakpoint): Add an early prototype. Improve debug output. (linux_attach): Doc update. (linux_detach_one_process, linux_detach): Clean up before releasing each process. (send_sigstop, wait_for_sigstop): Improve comments and debug output. * linux-low.h (struct process_info): Doc improvement. * mem-break.c (delete_all_breakpoints): New. * mem-break.h (delete_all_breakpoints): New prototype. * thread-db.c (find_first_thread): New. (thread_db_create_event): Call it instead of thread_db_find_new_threads. Clean up unused variables. (maybe_attach_thread): Remove first thread handling. (thread_db_find_new_threads): Use find_first_thread. (thread_db_get_tls_address): Likewise. --- gdb/gdbserver/ChangeLog | 19 +++++++ gdb/gdbserver/inferiors.c | 5 ++ gdb/gdbserver/linux-low.c | 44 +++++++++++++-- gdb/gdbserver/linux-low.h | 8 ++- gdb/gdbserver/mem-break.c | 9 ++++ gdb/gdbserver/mem-break.h | 4 ++ gdb/gdbserver/thread-db.c | 110 ++++++++++++++++++++++++-------------- 7 files changed, 153 insertions(+), 46 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 0f25df7c3f..7b78653323 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,22 @@ +2007-07-02 Daniel Jacobowitz + + * inferiors.c (change_inferior_id): Add comment. + * linux-low.c (check_removed_breakpoint): Add an early + prototype. Improve debug output. + (linux_attach): Doc update. + (linux_detach_one_process, linux_detach): Clean up before releasing + each process. + (send_sigstop, wait_for_sigstop): Improve comments and debug output. + * linux-low.h (struct process_info): Doc improvement. + * mem-break.c (delete_all_breakpoints): New. + * mem-break.h (delete_all_breakpoints): New prototype. + * thread-db.c (find_first_thread): New. + (thread_db_create_event): Call it instead of + thread_db_find_new_threads. Clean up unused variables. + (maybe_attach_thread): Remove first thread handling. + (thread_db_find_new_threads): Use find_first_thread. + (thread_db_get_tls_address): Likewise. + 2007-06-27 Daniel Jacobowitz * thread-db.c (thread_db_find_new_threads): Add prototype. diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index 63587e573b..6262d7e812 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -64,6 +64,11 @@ 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) diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 067a63290f..f37e407c94 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -68,6 +68,7 @@ static void linux_resume_one_process (struct inferior_list_entry *entry, 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); struct pending_signals { @@ -224,7 +225,8 @@ linux_attach (unsigned long pid) linux_attach_lwp (pid, pid); - /* Don't ignore the initial SIGSTOP if we just attached to this process. */ + /* Don't ignore the initial SIGSTOP if we just attached to this process. + It will be collected by wait shortly. */ process = (struct process_info *) find_inferior_id (&all_processes, pid); process->stop_expected = 0; @@ -286,13 +288,36 @@ linux_detach_one_process (struct inferior_list_entry *entry) struct thread_info *thread = (struct thread_info *) entry; struct process_info *process = get_thread_process (thread); + /* Make sure the process isn't stopped at a breakpoint that's + no longer there. */ + check_removed_breakpoint (process); + + /* If this process is stopped but is expecting a SIGSTOP, then make + sure we take care of that now. This isn't absolutely guaranteed + to collect the SIGSTOP, but is fairly likely to. */ + if (process->stop_expected) + { + /* Clear stop_expected, so that the SIGSTOP will be reported. */ + process->stop_expected = 0; + if (process->stopped) + linux_resume_one_process (&process->head, 0, 0, NULL); + linux_wait_for_event (thread); + } + + /* Flush any pending changes to the process's registers. */ + regcache_invalidate_one ((struct inferior_list_entry *) + get_process_thread (process)); + + /* Finally, let it resume. */ ptrace (PTRACE_DETACH, pid_of (process), 0, 0); } static int linux_detach (void) { + delete_all_breakpoints (); for_each_inferior (&all_threads, linux_detach_one_process); + clear_inferiors (); return 0; } @@ -332,7 +357,8 @@ check_removed_breakpoint (struct process_info *event_child) return 0; if (debug_threads) - fprintf (stderr, "Checking for breakpoint.\n"); + fprintf (stderr, "Checking for breakpoint in process %ld.\n", + event_child->lwpid); saved_inferior = current_inferior; current_inferior = get_process_thread (event_child); @@ -345,7 +371,8 @@ check_removed_breakpoint (struct process_info *event_child) if (stop_pc != event_child->pending_stop_pc) { if (debug_threads) - fprintf (stderr, "Ignoring, PC was changed.\n"); + fprintf (stderr, "Ignoring, PC was changed. Old PC was 0x%08llx\n", + event_child->pending_stop_pc); event_child->pending_is_breakpoint = 0; current_inferior = saved_inferior; @@ -817,6 +844,13 @@ send_sigstop (struct inferior_list_entry *entry) send another. */ if (process->stop_expected) { + if (debug_threads) + fprintf (stderr, "Have pending sigstop for process %ld\n", + process->lwpid); + + /* We clear the stop_expected flag so that wait_for_sigstop + will receive the SIGSTOP event (instead of silently resuming and + waiting again). It'll be reset below. */ process->stop_expected = 0; return; } @@ -852,7 +886,9 @@ wait_for_sigstop (struct inferior_list_entry *entry) && WSTOPSIG (wstat) != SIGSTOP) { if (debug_threads) - fprintf (stderr, "Stopped with non-sigstop signal\n"); + fprintf (stderr, "Process %ld (thread %ld) " + "stopped with non-sigstop status %06x\n", + process->lwpid, process->tid, wstat); process->status_pending_p = 1; process->status_pending = wstat; process->stop_expected = 1; diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index aa3bfe1268..d2e6c14f4b 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -92,8 +92,12 @@ struct process_info unsigned long lwpid; unsigned long tid; - /* If this flag is set, the next SIGSTOP will be ignored (the process will - be immediately resumed). */ + /* If this flag is set, the next SIGSTOP will be ignored (the + process will be immediately resumed). This means that either we + sent the SIGSTOP to it ourselves and got some other pending event + (so the SIGSTOP is still pending), or that we stopped the + inferior implicitly via PTRACE_ATTACH and have not waited for it + yet. */ int stop_expected; /* If this flag is set, the process is known to be stopped right now (stop diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 19233e0526..20281af7cd 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -283,3 +283,12 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len); } } + +/* Delete all breakpoints. */ + +void +delete_all_breakpoints (void) +{ + while (breakpoints) + delete_breakpoint (breakpoints); +} diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index 9c8dc28a28..7353807743 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -72,4 +72,8 @@ void check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len); void set_breakpoint_data (const unsigned char *bp_data, int bp_len); +/* Delete all breakpoints. */ + +void delete_all_breakpoints (void); + #endif /* MEM_BREAK_H */ diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index f3b85617ff..5566e0e812 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -41,7 +41,7 @@ static struct ps_prochandle proc_handle; /* Connection to the libthread_db library. */ static td_thragent_t *thread_agent; -static void thread_db_find_new_threads (void); +static int find_first_thread (void); static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); static char * @@ -135,15 +135,11 @@ thread_db_create_event (CORE_ADDR where) { td_event_msg_t msg; td_err_e err; - struct inferior_linux_data *tdata; - struct thread_info *inferior; struct process_info *process; if (debug_threads) fprintf (stderr, "Thread creation event.\n"); - tdata = inferior_target_data (current_inferior); - /* FIXME: This assumes we don't get another event. In the LinuxThreads implementation, this is safe, because all events come from the manager thread @@ -156,10 +152,9 @@ thread_db_create_event (CORE_ADDR where) /* If we do not know about the main thread yet, this would be a good time to find it. We need to do this to pick up the main thread before any newly created threads. */ - inferior = (struct thread_info *) all_threads.head; - process = get_thread_process (inferior); + process = get_thread_process (current_inferior); if (process->thread_known == 0) - thread_db_find_new_threads (); + find_first_thread (); /* msg.event == TD_EVENT_CREATE */ @@ -230,43 +225,73 @@ thread_db_enable_reporting () return 1; } -static void -maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) +static int +find_first_thread (void) { + td_thrhandle_t th; + td_thrinfo_t ti; td_err_e err; struct thread_info *inferior; struct process_info *process; - /* If we are attaching to our first thread, things are a little - different. */ - if (all_threads.head == all_threads.tail) + inferior = (struct thread_info *) all_threads.head; + process = get_thread_process (inferior); + if (process->thread_known) + return 1; + + /* Get information about the one thread we know we have. */ + 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)); + + err = td_thr_get_info (&th, &ti); + if (err != TD_OK) + error ("Cannot get first thread info: %s", thread_db_err_str (err)); + + if (debug_threads) + fprintf (stderr, "Found first 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); + + /* 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) { - inferior = (struct thread_info *) all_threads.head; - process = get_thread_process (inferior); - - if (process->thread_known == 0) - { - /* If the new thread ID is zero, a final thread ID will be - available later. Do not enable thread debugging yet. */ - if (ti_p->ti_tid == 0) - { - 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)); - return; - } - - if (process->lwpid != ti_p->ti_lid) - fatal ("PID mismatch! Expected %ld, got %ld", - (long) process->lwpid, (long) ti_p->ti_lid); - - /* Switch to indexing the threads list by TID. */ - change_inferior_id (&all_threads, ti_p->ti_tid); - goto found; - } + 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); + + process->tid = ti.ti_tid; + process->lwpid = ti.ti_lid; + process->thread_known = 1; + 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; +} + +static void +maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) +{ + td_err_e err; + struct thread_info *inferior; + struct process_info *process; + inferior = (struct thread_info *) find_inferior_id (&all_threads, ti_p->ti_tid); if (inferior != NULL) @@ -287,7 +312,6 @@ maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) process = inferior_target_data (inferior); -found: new_thread_notify (ti_p->ti_tid); process->tid = ti_p->ti_tid; @@ -325,6 +349,12 @@ thread_db_find_new_threads (void) { td_err_e err; + /* 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) + return; + /* Iterate over all user-space threads to discover new threads. */ err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, @@ -359,7 +389,7 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, process = get_thread_process (thread); if (!process->thread_known) - thread_db_find_new_threads (); + find_first_thread (); if (!process->thread_known) return TD_NOTHR; -- 2.34.1