diff options
Diffstat (limited to 'gdb/gdbserver/server.c')
-rw-r--r-- | gdb/gdbserver/server.c | 212 |
1 files changed, 170 insertions, 42 deletions
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 6edce81..5e80075 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -2016,6 +2016,63 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) } static void gdb_wants_all_threads_stopped (void); +static void resume (struct thread_resume *actions, size_t n); + +/* Call CALLBACK for any thread to which ACTIONS applies to. Returns + true if CALLBACK returns true. Returns false if no matching thread + is found or CALLBACK results false. */ + +static int +visit_actioned_threads (const struct thread_resume *actions, + size_t num_actions, + int (*callback) (const struct thread_resume *, + struct thread_info *)) +{ + struct inferior_list_entry *entry; + + for (entry = all_threads.head; entry != NULL; entry = entry->next) + { + size_t i; + + for (i = 0; i < num_actions; i++) + { + const struct thread_resume *action = &actions[i]; + + if (ptid_equal (action->thread, minus_one_ptid) + || ptid_equal (action->thread, entry->id) + || ((ptid_get_pid (action->thread) + == ptid_get_pid (entry->id)) + && ptid_get_lwp (action->thread) == -1)) + { + struct thread_info *thread = (struct thread_info *) entry; + + if ((*callback) (action, thread)) + return 1; + } + } + } + + return 0; +} + +/* Callback for visit_actioned_threads. If the thread has a pending + status to report, report it now. */ + +static int +handle_pending_status (const struct thread_resume *resumption, + struct thread_info *thread) +{ + if (thread->status_pending_p) + { + thread->status_pending_p = 0; + + last_status = thread->last_status; + last_ptid = thread->entry.id; + prepare_resume_reply (own_buf, last_ptid, &last_status); + return 1; + } + return 0; +} /* Parse vCont packets. */ void @@ -2128,12 +2185,34 @@ handle_v_cont (char *own_buf) cont_thread = minus_one_ptid; set_desired_inferior (0); + resume (resume_info, n); + free (resume_info); + return; + +err: + write_enn (own_buf); + free (resume_info); + return; +} + +/* Resume target with ACTIONS, an array of NUM_ACTIONS elements. */ + +static void +resume (struct thread_resume *actions, size_t num_actions) +{ if (!non_stop) - enable_async_io (); + { + /* Check if among the threads that GDB wants actioned, there's + one with a pending status to report. If so, skip actually + resuming/stopping and report the pending event + immediately. */ + if (visit_actioned_threads (actions, num_actions, handle_pending_status)) + return; - (*the_target->resume) (resume_info, n); + enable_async_io (); + } - free (resume_info); + (*the_target->resume) (actions, num_actions); if (non_stop) write_ok (own_buf); @@ -2157,12 +2236,6 @@ handle_v_cont (char *own_buf) || last_status.kind == TARGET_WAITKIND_SIGNALLED) mourn_inferior (find_process_pid (ptid_get_pid (last_ptid))); } - return; - -err: - write_enn (own_buf); - free (resume_info); - return; } /* Attach to a new program. Return 1 if successful, 0 if failure. */ @@ -2422,31 +2495,7 @@ myresume (char *own_buf, int step, int sig) n++; } - if (!non_stop) - enable_async_io (); - - (*the_target->resume) (resume_info, n); - - if (non_stop) - write_ok (own_buf); - else - { - last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); - - if (last_status.kind != TARGET_WAITKIND_EXITED - && last_status.kind != TARGET_WAITKIND_SIGNALLED) - { - current_inferior->last_resume_kind = resume_stop; - current_inferior->last_status = last_status; - } - - prepare_resume_reply (own_buf, last_ptid, &last_status); - disable_async_io (); - - if (last_status.kind == TARGET_WAITKIND_EXITED - || last_status.kind == TARGET_WAITKIND_SIGNALLED) - mourn_inferior (find_process_pid (ptid_get_pid (last_ptid))); - } + resume (resume_info, n); } /* Callback for for_each_inferior. Make a new stop reply for each @@ -2536,6 +2585,48 @@ gdb_reattached_process (struct inferior_list_entry *entry) process->gdb_detached = 0; } +/* Callback for for_each_inferior. Clear the thread's pending status + flag. */ + +static void +clear_pending_status_callback (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + + thread->status_pending_p = 0; +} + +/* Callback for for_each_inferior. If the thread is stopped with an + interesting event, mark it as having a pending event. */ + +static void +set_pending_status_callback (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + + if (thread->last_status.kind != TARGET_WAITKIND_STOPPED + || (thread->last_status.value.sig != GDB_SIGNAL_0 + /* A breakpoint, watchpoint or finished step from a previous + GDB run isn't considered interesting for a new GDB run. + If we left those pending, the new GDB could consider them + random SIGTRAPs. This leaves out real async traps. We'd + have to peek into the (target-specific) siginfo to + distinguish those. */ + && thread->last_status.value.sig != GDB_SIGNAL_TRAP)) + thread->status_pending_p = 1; +} + +/* Callback for find_inferior. Return true if ENTRY (a thread) has a + pending status to report to GDB. */ + +static int +find_status_pending_thread_callback (struct inferior_list_entry *entry, void *data) +{ + struct thread_info *thread = (struct thread_info *) entry; + + return thread->status_pending_p; +} + /* Status handler for the '?' packet. */ static void @@ -2544,13 +2635,15 @@ handle_status (char *own_buf) /* GDB is connected, don't forward events to the target anymore. */ for_each_inferior (&all_processes, gdb_reattached_process); + discard_queued_stop_replies (-1); + for_each_inferior (&all_threads, clear_pending_status_callback); + /* In non-stop mode, we must send a stop reply for each stopped thread. In all-stop mode, just send one for the first stopped thread we find. */ if (non_stop) { - discard_queued_stop_replies (-1); find_inferior (&all_threads, queue_stop_reply_callback, NULL); /* The first is sent immediatly. OK is sent if there is no @@ -2560,18 +2653,53 @@ handle_status (char *own_buf) } else { + struct inferior_list_entry *thread = NULL; + pause_all (0); stabilize_threads (); gdb_wants_all_threads_stopped (); - if (all_threads.head) - { - struct target_waitstatus status; + /* We can only report one status, but we might be coming out of + non-stop -- if more than one thread is stopped with + interesting events, leave events for the threads we're not + reporting now pending. They'll be reported the next time the + threads are resumed. Start by marking all interesting events + as pending. */ + for_each_inferior (&all_threads, set_pending_status_callback); + + /* Prefer the last thread that reported an event to GDB (even if + that was a GDB_SIGNAL_TRAP). */ + if (last_status.kind != TARGET_WAITKIND_IGNORE + && last_status.kind != TARGET_WAITKIND_EXITED + && last_status.kind != TARGET_WAITKIND_SIGNALLED) + thread = find_inferior_id (&all_threads, last_ptid); + + /* If the last event thread is not found for some reason, look + for some other thread that might have an event to report. */ + if (thread == NULL) + thread = find_inferior (&all_threads, + find_status_pending_thread_callback, NULL); + + /* If we're still out of luck, simply pick the first thread in + the thread list. */ + if (thread == NULL) + thread = all_threads.head; + + if (thread != NULL) + { + struct thread_info *tp = (struct thread_info *) thread; + + /* We're reporting this event, so it's no longer + pending. */ + tp->status_pending_p = 0; + + /* GDB assumes the current thread is the thread we're + reporting the status for. */ + general_thread = thread->id; + set_desired_inferior (1); - status.kind = TARGET_WAITKIND_STOPPED; - status.value.sig = GDB_SIGNAL_TRAP; - prepare_resume_reply (own_buf, - all_threads.head->id, &status); + gdb_assert (tp->last_status.kind != TARGET_WAITKIND_IGNORE); + prepare_resume_reply (own_buf, tp->entry.id, &tp->last_status); } else strcpy (own_buf, "W00"); |