From 5544ad8993e779a67e70bdb9b03e12e94ac82d80 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sat, 31 Jan 2004 22:19:32 +0000 Subject: * linux-low.c: Update copyright year. (check_removed_breakpoint): Clear pending_is_breakpoint. (linux_set_resume_request, linux_queue_one_thread) (resume_status_pending_p): New functions. (linux_continue_one_thread): Use process->resume. (linux_resume): Only resume threads if there are no pending events. * linux-low.h (struct process_info): Add resume request pointer. --- gdb/gdbserver/ChangeLog | 11 ++++ gdb/gdbserver/linux-low.c | 137 ++++++++++++++++++++++++++++++++++++++++------ gdb/gdbserver/linux-low.h | 8 ++- 3 files changed, 138 insertions(+), 18 deletions(-) (limited to 'gdb') diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index ea3fca9..0d908ea 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,14 @@ +2004-01-31 Daniel Jacobowitz + + * linux-low.c: Update copyright year. + (check_removed_breakpoint): Clear pending_is_breakpoint. + (linux_set_resume_request, linux_queue_one_thread) + (resume_status_pending_p): New functions. + (linux_continue_one_thread): Use process->resume. + (linux_resume): Only resume threads if there are no pending events. + * linux-low.h (struct process_info): Add resume request + pointer. + 2004-01-30 Daniel Jacobowitz * regcache.c (new_register_cache): Clear the allocated register diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 68b3f65..501bb58 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -1,5 +1,5 @@ /* Low level interface to ptrace, for the remote server for GDB. - Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002 + Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -316,6 +316,7 @@ check_removed_breakpoint (struct process_info *event_child) (*the_low_target.set_pc) (stop_pc); /* We consumed the pending SIGTRAP. */ + event_child->pending_is_breakpoint = 0; event_child->status_pending_p = 0; event_child->status_pending = 0; @@ -876,18 +877,18 @@ linux_resume_one_process (struct inferior_list_entry *entry, static struct thread_resume *resume_ptr; /* This function is called once per thread. We look up the thread - in RESUME_PTR, which will tell us whether to resume, step, or leave - the thread stopped; and what signal, if any, it should be sent. - For threads which we aren't explicitly told otherwise, we preserve - the stepping flag; this is used for stepping over gdbserver-placed - breakpoints. If the thread has a status pending, it may not actually - be resumed. */ + in RESUME_PTR, and mark the thread with a pointer to the appropriate + resume request. + + This algorithm is O(threads * resume elements), but resume elements + is small (and will remain small at least until GDB supports thread + suspension). */ static void -linux_continue_one_thread (struct inferior_list_entry *entry) +linux_set_resume_request (struct inferior_list_entry *entry) { struct process_info *process; struct thread_info *thread; - int ndx, step; + int ndx; thread = (struct thread_info *) entry; process = get_thread_process (thread); @@ -896,25 +897,127 @@ linux_continue_one_thread (struct inferior_list_entry *entry) while (resume_ptr[ndx].thread != -1 && resume_ptr[ndx].thread != entry->id) ndx++; - if (resume_ptr[ndx].leave_stopped) + process->resume = &resume_ptr[ndx]; +} + +/* This function is called once per thread. We check the thread's resume + request, which will tell us whether to resume, step, or leave the thread + stopped; and what signal, if any, it should be sent. For threads which + we aren't explicitly told otherwise, we preserve the stepping flag; this + is used for stepping over gdbserver-placed breakpoints. */ + +static void +linux_continue_one_thread (struct inferior_list_entry *entry) +{ + struct process_info *process; + struct thread_info *thread; + int step; + + thread = (struct thread_info *) entry; + process = get_thread_process (thread); + + if (process->resume->leave_stopped) return; - if (resume_ptr[ndx].thread == -1) - step = process->stepping || resume_ptr[ndx].step; + if (process->resume->thread == -1) + step = process->stepping || process->resume->step; else - step = resume_ptr[ndx].step; + step = process->resume->step; + + linux_resume_one_process (&process->head, step, process->resume->sig); - linux_resume_one_process (&process->head, step, resume_ptr[ndx].sig); + process->resume = NULL; +} + +/* This function is called once per thread. We check the thread's resume + request, which will tell us whether to resume, step, or leave the thread + stopped; and what signal, if any, it should be sent. We queue any needed + signals, since we won't actually resume. We already have a pending event + to report, so we don't need to preserve any step requests; they should + be re-issued if necessary. */ + +static void +linux_queue_one_thread (struct inferior_list_entry *entry) +{ + struct process_info *process; + struct thread_info *thread; + + thread = (struct thread_info *) entry; + process = get_thread_process (thread); + + if (process->resume->leave_stopped) + return; + + /* If we have a new signal, enqueue the signal. */ + if (process->resume->sig != 0) + { + struct pending_signals *p_sig; + p_sig = malloc (sizeof (*p_sig)); + p_sig->prev = process->pending_signals; + p_sig->signal = process->resume->sig; + process->pending_signals = p_sig; + } + + process->resume = NULL; +} + +/* Set DUMMY if this process has an interesting status pending. */ +static int +resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p) +{ + struct process_info *process = (struct process_info *) entry; + + /* Processes which will not be resumed are not interesting, because + we might not wait for them next time through linux_wait. */ + if (process->resume->leave_stopped) + return 0; + + /* If this thread has a removed breakpoint, we won't have any + events to report later, so check now. check_removed_breakpoint + may clear status_pending_p. We avoid calling check_removed_breakpoint + for any thread that we are not otherwise going to resume - this + lets us preserve stopped status when two threads hit a breakpoint. + GDB removes the breakpoint to single-step a particular thread + past it, then re-inserts it and resumes all threads. We want + to report the second thread without resuming it in the interim. */ + if (process->status_pending_p) + check_removed_breakpoint (process); + + if (process->status_pending_p) + * (int *) flag_p = 1; + + return 0; } static void linux_resume (struct thread_resume *resume_info) { - /* Yes, this is quadratic. If it ever becomes a problem then it's - fairly easy to fix. Yes, the use of a global here is rather ugly. */ + int pending_flag; + /* Yes, the use of a global here is rather ugly. */ resume_ptr = resume_info; - for_each_inferior (&all_threads, linux_continue_one_thread); + + for_each_inferior (&all_threads, linux_set_resume_request); + + /* If there is a thread which would otherwise be resumed, which + has a pending status, then don't resume any threads - we can just + report the pending status. Make sure to queue any signals + that would otherwise be sent. */ + pending_flag = 0; + find_inferior (&all_processes, resume_status_pending_p, &pending_flag); + + if (debug_threads) + { + if (pending_flag) + fprintf (stderr, "Not resuming, pending status\n"); + else + fprintf (stderr, "Resuming, no pending status\n"); + } + + if (pending_flag) + for_each_inferior (&all_threads, linux_queue_one_thread); + else + for_each_inferior (&all_threads, linux_continue_one_thread); } #ifdef HAVE_LINUX_USRREGS diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index bae76b7..d42c9b5 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -1,5 +1,5 @@ /* Internal interfaces for the GNU/Linux specific target code for gdbserver. - Copyright 2002, Free Software Foundation, Inc. + Copyright 2002, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -106,7 +106,13 @@ struct process_info /* If this is non-zero, it points to a chain of signals which need to be delivered to this process. */ struct pending_signals *pending_signals; + + /* A link used when resuming. It is initialized from the resume request, + and then processed and cleared in linux_resume_one_process. */ + + struct thread_resume *resume; }; + extern struct inferior_list all_processes; void linux_attach_lwp (int pid, int tid); -- cgit v1.1