From f9e39928dc25d4f8ec5cad87c6cea4cd5ef08f58 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Sun, 2 May 2010 00:47:34 +0000 Subject: [PATCH] * linux-low.c (linux_kill_one_lwp): Assume the lwp is stopped. (linux_kill): Stop all lwps here. Don't delete the main lwp here. (linux_detach_one_lwp): Assume the lwp is stopped. (any_thread_of): Delete. (linux_detach): Stop all lwps here. Don't blindly delete all breakpoints. (delete_lwp_callback): New. (linux_mourn): Delete all lwps of the process that is gone. (linux_wait_1): Don't delete the last lwp of the process here. * mem-break.h (mark_breakpoints_out): Declare. * mem-break.c (mark_breakpoints_out): New. (free_all_breakpoints): Use it. * server.c (handle_target_event): If the process is gone, mark breakpoints out. * thread-db.c (struct thread_db) : New field. (thread_db_enable_reporting): Fix prototype. Store a thread event breakpoint reference in the thread_db struct. (thread_db_load_search): Clear the thread_db object. (try_thread_db_load_1): Ditto. (switch_to_process): New. (disable_thread_event_reporting): Use it. (remove_thread_event_breakpoints): New. (thread_db_detach, thread_db_mourn): Use it. --- gdb/gdbserver/ChangeLog | 26 +++++++++++ gdb/gdbserver/linux-low.c | 90 +++++++++++++++------------------------ gdb/gdbserver/mem-break.c | 14 ++++-- gdb/gdbserver/mem-break.h | 4 ++ gdb/gdbserver/server.c | 5 ++- gdb/gdbserver/thread-db.c | 66 +++++++++++++++++++++++----- 6 files changed, 135 insertions(+), 70 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 15f2176df3..a1f479857b 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,29 @@ +2010-05-02 Pedro Alves + + * linux-low.c (linux_kill_one_lwp): Assume the lwp is stopped. + (linux_kill): Stop all lwps here. Don't delete the main lwp here. + (linux_detach_one_lwp): Assume the lwp is stopped. + (any_thread_of): Delete. + (linux_detach): Stop all lwps here. Don't blindly delete all + breakpoints. + (delete_lwp_callback): New. + (linux_mourn): Delete all lwps of the process that is gone. + (linux_wait_1): Don't delete the last lwp of the process here. + * mem-break.h (mark_breakpoints_out): Declare. + * mem-break.c (mark_breakpoints_out): New. + (free_all_breakpoints): Use it. + * server.c (handle_target_event): If the process is gone, mark + breakpoints out. + * thread-db.c (struct thread_db) : New field. + (thread_db_enable_reporting): Fix prototype. Store a thread event + breakpoint reference in the thread_db struct. + (thread_db_load_search): Clear the thread_db object. + (try_thread_db_load_1): Ditto. + (switch_to_process): New. + (disable_thread_event_reporting): Use it. + (remove_thread_event_breakpoints): New. + (thread_db_detach, thread_db_mourn): Use it. + 2010-05-01 Pedro Alves * linux-low.c (linux_enable_event_reporting): New. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 302f702857..65a87f68ed 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -739,11 +739,6 @@ linux_kill_one_lwp (struct inferior_list_entry *entry, void *args) return 0; } - /* If we're killing a running inferior, make sure it is stopped - first, as PTRACE_KILL will not work otherwise. */ - if (!lwp->stopped) - send_sigstop (lwp); - do { ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); @@ -768,6 +763,10 @@ linux_kill (int pid) if (process == NULL) return -1; + /* If we're killing a running inferior, make sure it is stopped + first, as PTRACE_KILL will not work otherwise. */ + stop_all_lwps (); + find_inferior (&all_threads, linux_kill_one_lwp, &pid); /* See the comment in linux_kill_one_lwp. We did not kill the first @@ -779,11 +778,6 @@ linux_kill (int pid) fprintf (stderr, "lk_1: killing lwp %ld, for pid: %d\n", lwpid_of (lwp), pid); - /* If we're killing a running inferior, make sure it is stopped - first, as PTRACE_KILL will not work otherwise. */ - if (!lwp->stopped) - send_sigstop (lwp); - do { ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); @@ -792,9 +786,11 @@ linux_kill (int pid) lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL); } while (lwpid > 0 && WIFSTOPPED (wstat)); - delete_lwp (lwp); - the_target->mourn (process); + + /* Since we presently can only stop all lwps of all processes, we + need to unstop lwps of other processes. */ + unstop_all_lwps (NULL); return 0; } @@ -808,28 +804,6 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) if (ptid_get_pid (entry->id) != pid) return 0; - /* If we're detaching from a running inferior, make sure it is - stopped first, as PTRACE_DETACH will not work otherwise. */ - if (!lwp->stopped) - { - int lwpid = lwpid_of (lwp); - - stopping_threads = 1; - send_sigstop (lwp); - - /* If this detects a new thread through a clone event, the new - thread is appended to the end of the lwp list, so we'll - eventually detach from it. */ - wait_for_sigstop (&lwp->head); - stopping_threads = 0; - - /* If LWP exits while we're trying to stop it, there's nothing - left to do. */ - lwp = find_lwp_pid (pid_to_ptid (lwpid)); - if (lwp == NULL) - return 0; - } - /* 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. */ @@ -838,8 +812,7 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) int wstat; /* Clear stop_expected, so that the SIGSTOP will be reported. */ lwp->stop_expected = 0; - if (lwp->stopped) - linux_resume_one_lwp (lwp, 0, 0, NULL); + linux_resume_one_lwp (lwp, 0, 0, NULL); linux_wait_for_event (lwp->head.id, &wstat, __WALL); } @@ -854,17 +827,6 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) return 0; } -static int -any_thread_of (struct inferior_list_entry *entry, void *args) -{ - int *pid_p = args; - - if (ptid_get_pid (entry->id) == *pid_p) - return 1; - - return 0; -} - static int linux_detach (int pid) { @@ -874,17 +836,37 @@ linux_detach (int pid) if (process == NULL) return -1; + /* Stop all threads before detaching. First, ptrace requires that + the thread is stopped to sucessfully detach. Second, thread_db + may need to uninstall thread event breakpoints from memory, which + only works with a stopped process anyway. */ + stop_all_lwps (); + #ifdef USE_THREAD_DB thread_db_detach (process); #endif - current_inferior = - (struct thread_info *) find_inferior (&all_threads, any_thread_of, &pid); - - delete_all_breakpoints (); find_inferior (&all_threads, linux_detach_one_lwp, &pid); the_target->mourn (process); + + /* Since we presently can only stop all lwps of all processes, we + need to unstop lwps of other processes. */ + unstop_all_lwps (NULL); + return 0; +} + +/* Remove all LWPs that belong to process PROC from the lwp list. */ + +static int +delete_lwp_callback (struct inferior_list_entry *entry, void *proc) +{ + struct lwp_info *lwp = (struct lwp_info *) entry; + struct process_info *process = proc; + + if (pid_of (lwp) == pid_of (process)) + delete_lwp (lwp); + return 0; } @@ -897,6 +879,8 @@ linux_mourn (struct process_info *process) thread_db_mourn (process); #endif + find_inferior (&all_lwps, delete_lwp_callback, process); + /* Freeing all private data. */ priv = process->private; free (priv->arch_private); @@ -1695,10 +1679,6 @@ retry: { if (WIFEXITED (w) || WIFSIGNALED (w)) { - delete_lwp (event_child); - - current_inferior = NULL; - if (WIFEXITED (w)) { ourstatus->kind = TARGET_WAITKIND_EXITED; diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index c796948ec8..3d7382ecde 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -716,16 +716,24 @@ delete_all_breakpoints (void) delete_breakpoint_1 (proc, proc->breakpoints); } -/* Release all breakpoints, but do not try to un-insert them from the - inferior. */ +/* Clear the "inserted" flag in all breakpoints. */ void -free_all_breakpoints (struct process_info *proc) +mark_breakpoints_out (struct process_info *proc) { struct raw_breakpoint *raw_bp; for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next) raw_bp->inserted = 0; +} + +/* Release all breakpoints, but do not try to un-insert them from the + inferior. */ + +void +free_all_breakpoints (struct process_info *proc) +{ + mark_breakpoints_out (proc); /* Note: use PROC explicitly instead of deferring to delete_all_breakpoints --- CURRENT_INFERIOR may already have been diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index b9fcf651c4..0bd86b5d89 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -103,6 +103,10 @@ void set_breakpoint_data (const unsigned char *bp_data, int bp_len); void delete_all_breakpoints (void); +/* Clear the "inserted" flag in all breakpoints of PROC. */ + +void mark_breakpoints_out (struct process_info *proc); + /* Delete all breakpoints, but do not try to un-insert them from the inferior. */ diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 460cb46bec..ffca3977f3 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -3031,7 +3031,10 @@ handle_target_event (int err, gdb_client_data client_data) if (last_status.kind == TARGET_WAITKIND_EXITED || last_status.kind == TARGET_WAITKIND_SIGNALLED) - mourn_inferior (process); + { + mark_breakpoints_out (process); + mourn_inferior (process); + } if (forward_event) { diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 8e7d7a9361..7dec30dafa 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -52,6 +52,16 @@ struct thread_db void *handle; #endif + /* 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 breakpoint at this + location, GDB can detect when a new thread is created. We obtain + this location via the td_ta_event_addr call. Note that if the + running kernel supports tracing clones, then we don't need to use + (and in fact don't use) this magic thread event breakpoint to + learn about threads. */ + struct breakpoint *td_create_bp; + /* Addresses of libthread_db functions. */ td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, td_thragent_t **ta); td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta, @@ -205,7 +215,7 @@ thread_db_create_event (CORE_ADDR where) } static int -thread_db_enable_reporting () +thread_db_enable_reporting (void) { td_thr_events_t events; td_notify_t notify; @@ -239,8 +249,9 @@ thread_db_enable_reporting () thread_db_err_str (err)); return 0; } - set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, - thread_db_create_event); + thread_db->td_create_bp + = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, + thread_db_create_event); return 1; } @@ -501,6 +512,8 @@ thread_db_load_search (void) if (proc->private->thread_db != NULL) fatal ("unexpected: proc->private->thread_db != NULL"); + memset (&tdb, 0, sizeof (tdb)); + tdb.td_ta_new_p = &td_ta_new; /* Attempt to open a connection to the thread library. */ @@ -544,6 +557,8 @@ try_thread_db_load_1 (void *handle) if (proc->private->thread_db != NULL) fatal ("unexpected: proc->private->thread_db != NULL"); + memset (&tdb, 0, sizeof (tdb)); + tdb.handle = handle; /* Initialize pointers to the dynamic library functions we will use. @@ -766,6 +781,16 @@ any_thread_of (struct inferior_list_entry *entry, void *args) return 0; } +static void +switch_to_process (struct process_info *proc) +{ + int pid = pid_of (proc); + + current_inferior = + (struct thread_info *) find_inferior (&all_threads, + any_thread_of, &pid); +} + /* Disconnect from libthread_db and free resources. */ static void @@ -785,15 +810,10 @@ disable_thread_event_reporting (struct process_info *proc) if (td_ta_clear_event_p != NULL) { - struct thread_info *saved_inferior; + struct thread_info *saved_inferior = current_inferior; td_thr_events_t events; - int pid; - pid = pid_of (proc); - saved_inferior = current_inferior; - current_inferior = - (struct thread_info *) find_inferior (&all_threads, - any_thread_of, &pid); + switch_to_process (proc); /* Set the process wide mask saying we aren't interested in any events anymore. */ @@ -805,10 +825,34 @@ disable_thread_event_reporting (struct process_info *proc) } } +static void +remove_thread_event_breakpoints (struct process_info *proc) +{ + struct thread_db *thread_db = proc->private->thread_db; + + if (thread_db->td_create_bp != NULL) + { + struct thread_info *saved_inferior = current_inferior; + + switch_to_process (proc); + + delete_breakpoint (thread_db->td_create_bp); + thread_db->td_create_bp = NULL; + + current_inferior = saved_inferior; + } +} + void thread_db_detach (struct process_info *proc) { - disable_thread_event_reporting (proc); + struct thread_db *thread_db = proc->private->thread_db; + + if (thread_db) + { + disable_thread_event_reporting (proc); + remove_thread_event_breakpoints (proc); + } } /* Disconnect from libthread_db and free resources. */ -- 2.34.1