X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fthread.c;h=378c5ee2d133ea17eef42c8d9041fc260c424fc0;hb=3c4e2282566d54b974f6929ee5c1270fb3cea4e5;hp=cde7c34df692741127da848b306be02edd43b142;hpb=a70b814420059e1f2de2130d532ddd7b2b2500fc;p=binutils-gdb.git diff --git a/gdb/thread.c b/gdb/thread.c index cde7c34df69..378c5ee2d13 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1,6 +1,6 @@ /* Multi-process/thread control for GDB, the GNU debugger. - Copyright (C) 1986-2019 Free Software Foundation, Inc. + Copyright (C) 1986-2022 Free Software Foundation, Inc. Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA. @@ -23,7 +23,7 @@ #include "symtab.h" #include "frame.h" #include "inferior.h" -#include "common/environ.h" +#include "gdbsupport/environ.h" #include "value.h" #include "target.h" #include "gdbthread.h" @@ -39,59 +39,51 @@ #include "observable.h" #include "annotate.h" #include "cli/cli-decode.h" -#include "gdb_regex.h" +#include "cli/cli-option.h" +#include "gdbsupport/gdb_regex.h" #include "cli/cli-utils.h" #include "thread-fsm.h" #include "tid-parse.h" #include -#include "common/gdb_optional.h" +#include "gdbsupport/gdb_optional.h" #include "inline-frame.h" +#include "stack.h" -/* Definition of struct thread_info exported to gdbthread.h. */ +/* See gdbthread.h. */ -/* Prototypes for local functions. */ +bool debug_threads = false; -static int highest_thread_num; +/* Implement 'show debug threads'. */ -/* True if any thread is, or may be executing. We need to track this - separately because until we fully sync the thread list, we won't - know whether the target is fully stopped, even if we see stop - events for all known threads, because any of those threads may have - spawned new threads we haven't heard of yet. */ -static int threads_executing; +static void +show_debug_threads (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + gdb_printf (file, _("Thread debugging is \"%s\".\n"), value); +} -static int thread_alive (struct thread_info *); +/* Definition of struct thread_info exported to gdbthread.h. */ -/* RAII type used to increase / decrease the refcount of each thread - in a given list of threads. */ +/* Prototypes for local functions. */ -class scoped_inc_dec_ref -{ -public: - explicit scoped_inc_dec_ref (const std::vector &thrds) - : m_thrds (thrds) - { - for (thread_info *thr : m_thrds) - thr->incref (); - } +static int highest_thread_num; - ~scoped_inc_dec_ref () - { - for (thread_info *thr : m_thrds) - thr->decref (); - } +/* The current/selected thread. */ +static thread_info *current_thread_; -private: - const std::vector &m_thrds; -}; +/* Returns true if THR is the current thread. */ +static bool +is_current_thread (const thread_info *thr) +{ + return thr == current_thread_; +} struct thread_info* inferior_thread (void) { - struct thread_info *tp = find_thread_ptid (inferior_ptid); - gdb_assert (tp); - return tp; + gdb_assert (current_thread_ != nullptr); + return current_thread_; } /* Delete the breakpoint pointed at by BP_P, if there's one. */ @@ -168,11 +160,10 @@ thread_has_single_step_breakpoint_here (struct thread_info *tp, void thread_cancel_execution_command (struct thread_info *thr) { - if (thr->thread_fsm != NULL) + if (thr->thread_fsm () != nullptr) { - thr->thread_fsm->clean_up (thr); - delete thr->thread_fsm; - thr->thread_fsm = NULL; + std::unique_ptr fsm = thr->release_thread_fsm (); + fsm->clean_up (thr); } } @@ -195,20 +186,31 @@ clear_thread_inferior_resources (struct thread_info *tp) thread_cancel_execution_command (tp); - clear_inline_frame_state (tp->ptid); + clear_inline_frame_state (tp); } -/* Set the TP's state as exited. */ +/* See gdbthread.h. */ -static void -set_thread_exited (thread_info *tp, int silent) +void +set_thread_exited (thread_info *tp, bool silent) { - /* Dead threads don't need to step-over. Remove from queue. */ - if (tp->step_over_next != NULL) - thread_step_over_chain_remove (tp); + /* Dead threads don't need to step-over. Remove from chain. */ + if (thread_is_in_step_over_chain (tp)) + global_thread_step_over_chain_remove (tp); if (tp->state != THREAD_EXITED) { + process_stratum_target *proc_target = tp->inf->process_target (); + + /* Some targets unpush themselves from the inferior's target stack before + clearing the inferior's thread list (which marks all threads as exited, + and therefore leads to this function). In this case, the inferior's + process target will be nullptr when we arrive here. + + See also the comment in inferior::unpush_target. */ + if (proc_target != nullptr) + proc_target->maybe_remove_resumed_with_pending_wait_status (tp); + gdb::observers::thread_exit.notify (tp, silent); /* Tag it as exited. */ @@ -216,6 +218,14 @@ set_thread_exited (thread_info *tp, int silent) /* Clear breakpoints, etc. associated with this thread. */ clear_thread_inferior_resources (tp); + + /* Remove from the ptid_t map. We don't want for + find_thread_ptid to find exited threads. Also, the target + may reuse the ptid for a new thread, and there can only be + one value per key; adding a new thread with the same ptid_t + would overwrite the exited thread's ptid entry. */ + size_t nr_deleted = tp->inf->ptid_thread_map.erase (tp->ptid); + gdb_assert (nr_deleted == 1); } } @@ -224,17 +234,8 @@ init_thread_list (void) { highest_thread_num = 0; - for (thread_info *tp : all_threads_safe ()) - { - inferior *inf = tp->inf; - - if (tp->deletable ()) - delete tp; - else - set_thread_exited (tp, 1); - - inf->thread_list = NULL; - } + for (inferior *inf : all_inferiors ()) + inf->clear_thread_list (true); } /* Allocate a new thread of inferior INF with target id PTID and add @@ -245,65 +246,37 @@ new_thread (struct inferior *inf, ptid_t ptid) { thread_info *tp = new thread_info (inf, ptid); - if (inf->thread_list == NULL) - inf->thread_list = tp; - else - { - struct thread_info *last; + threads_debug_printf ("creating a new thread object, inferior %d, ptid %s", + inf->num, ptid.to_string ().c_str ()); - for (last = inf->thread_list; last->next != NULL; last = last->next) - ; - last->next = tp; - } + inf->thread_list.push_back (*tp); + + /* A thread with this ptid should not exist in the map yet. */ + gdb_assert (inf->ptid_thread_map.find (ptid) == inf->ptid_thread_map.end ()); + + inf->ptid_thread_map[ptid] = tp; return tp; } struct thread_info * -add_thread_silent (ptid_t ptid) +add_thread_silent (process_stratum_target *targ, ptid_t ptid) { - struct inferior *inf = find_inferior_ptid (ptid); - gdb_assert (inf != NULL); - - thread_info *tp = find_thread_ptid (inf, ptid); - if (tp) - /* Found an old thread with the same id. It has to be dead, - otherwise we wouldn't be adding a new thread with the same id. - The OS is reusing this id --- delete it, and recreate a new - one. */ - { - /* In addition to deleting the thread, if this is the current - thread, then we need to take care that delete_thread doesn't - really delete the thread if it is inferior_ptid. Create a - new template thread in the list with an invalid ptid, switch - to it, delete the original thread, reset the new thread's - ptid, and switch to it. */ - - if (inferior_ptid == ptid) - { - thread_info *new_thr = new_thread (inf, null_ptid); + gdb_assert (targ != nullptr); - /* Make switch_to_thread not read from the thread. */ - new_thr->state = THREAD_EXITED; - switch_to_no_thread (); + inferior *inf = find_inferior_ptid (targ, ptid); - /* Now we can delete it. */ - delete_thread (tp); + threads_debug_printf ("add thread to inferior %d, ptid %s, target %s", + inf->num, ptid.to_string ().c_str (), + targ->shortname ()); - /* Now reset its ptid, and reswitch inferior_ptid to it. */ - new_thr->ptid = ptid; - new_thr->state = THREAD_STOPPED; - switch_to_thread (new_thr); - - gdb::observers::new_thread.notify (new_thr); - - /* All done. */ - return new_thr; - } - else - /* Just go ahead and delete it. */ - delete_thread (tp); - } + /* We may have an old thread with the same id in the thread list. + If we do, it must be dead, otherwise we wouldn't be adding a new + thread with the same id. The OS is reusing this id --- delete + the old thread, and create a new one. */ + thread_info *tp = find_thread_ptid (inf, ptid); + if (tp != nullptr) + delete_thread (tp); tp = new_thread (inf, ptid); gdb::observers::new_thread.notify (tp); @@ -312,23 +285,24 @@ add_thread_silent (ptid_t ptid) } struct thread_info * -add_thread_with_info (ptid_t ptid, private_thread_info *priv) +add_thread_with_info (process_stratum_target *targ, ptid_t ptid, + private_thread_info *priv) { - struct thread_info *result = add_thread_silent (ptid); + thread_info *result = add_thread_silent (targ, ptid); result->priv.reset (priv); if (print_thread_events) - printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid).c_str ()); + gdb_printf (_("[New %s]\n"), target_pid_to_str (ptid).c_str ()); annotate_new_thread (); return result; } struct thread_info * -add_thread (ptid_t ptid) +add_thread (process_stratum_target *targ, ptid_t ptid) { - return add_thread_with_info (ptid, NULL); + return add_thread_with_info (targ, ptid, NULL); } private_thread_info::~private_thread_info () = default; @@ -342,14 +316,14 @@ thread_info::thread_info (struct inferior *inf_, ptid_t ptid_) this->per_inf_num = ++inf_->highest_thread_num; /* Nothing to follow yet. */ - memset (&this->pending_follow, 0, sizeof (this->pending_follow)); - this->pending_follow.kind = TARGET_WAITKIND_SPURIOUS; - this->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE; + this->pending_follow.set_spurious (); } +/* See gdbthread.h. */ + thread_info::~thread_info () { - xfree (this->name); + threads_debug_printf ("thread %s", this->ptid.to_string ().c_str ()); } /* See gdbthread.h. */ @@ -359,63 +333,67 @@ thread_info::deletable () const { /* If this is the current thread, or there's code out there that relies on it existing (refcount > 0) we can't delete yet. */ - return refcount () == 0 && ptid != inferior_ptid; + return refcount () == 0 && !is_current_thread (this); } -/* Add TP to the end of the step-over chain LIST_P. */ +/* See gdbthread.h. */ -static void -step_over_chain_enqueue (struct thread_info **list_p, struct thread_info *tp) +void +thread_info::set_executing (bool executing) { - gdb_assert (tp->step_over_next == NULL); - gdb_assert (tp->step_over_prev == NULL); + m_executing = executing; + if (executing) + this->clear_stop_pc (); +} - if (*list_p == NULL) - { - *list_p = tp; - tp->step_over_prev = tp->step_over_next = tp; - } - else - { - struct thread_info *head = *list_p; - struct thread_info *tail = head->step_over_prev; +/* See gdbthread.h. */ - tp->step_over_prev = tail; - tp->step_over_next = head; - head->step_over_prev = tp; - tail->step_over_next = tp; - } +void +thread_info::set_resumed (bool resumed) +{ + if (resumed == m_resumed) + return; + + process_stratum_target *proc_target = this->inf->process_target (); + + /* If we transition from resumed to not resumed, we might need to remove + the thread from the resumed threads with pending statuses list. */ + if (!resumed) + proc_target->maybe_remove_resumed_with_pending_wait_status (this); + + m_resumed = resumed; + + /* If we transition from not resumed to resumed, we might need to add + the thread to the resumed threads with pending statuses list. */ + if (resumed) + proc_target->maybe_add_resumed_with_pending_wait_status (this); } -/* Remove TP from step-over chain LIST_P. */ +/* See gdbthread.h. */ -static void -step_over_chain_remove (struct thread_info **list_p, struct thread_info *tp) +void +thread_info::set_pending_waitstatus (const target_waitstatus &ws) { - gdb_assert (tp->step_over_next != NULL); - gdb_assert (tp->step_over_prev != NULL); + gdb_assert (!this->has_pending_waitstatus ()); - if (*list_p == tp) - { - if (tp == tp->step_over_next) - *list_p = NULL; - else - *list_p = tp->step_over_next; - } + m_suspend.waitstatus = ws; + m_suspend.waitstatus_pending_p = 1; - tp->step_over_prev->step_over_next = tp->step_over_next; - tp->step_over_next->step_over_prev = tp->step_over_prev; - tp->step_over_prev = tp->step_over_next = NULL; + process_stratum_target *proc_target = this->inf->process_target (); + proc_target->maybe_add_resumed_with_pending_wait_status (this); } /* See gdbthread.h. */ -struct thread_info * -thread_step_over_chain_next (struct thread_info *tp) +void +thread_info::clear_pending_waitstatus () { - struct thread_info *next = tp->step_over_next; + gdb_assert (this->has_pending_waitstatus ()); - return (next == step_over_queue_head ? NULL : next); + process_stratum_target *proc_target = this->inf->process_target (); + proc_target->maybe_remove_resumed_with_pending_wait_status (this); + + m_suspend.waitstatus_pending_p = 0; } /* See gdbthread.h. */ @@ -423,26 +401,56 @@ thread_step_over_chain_next (struct thread_info *tp) int thread_is_in_step_over_chain (struct thread_info *tp) { - return (tp->step_over_next != NULL); + return tp->step_over_list_node.is_linked (); +} + +/* See gdbthread.h. */ + +int +thread_step_over_chain_length (const thread_step_over_list &l) +{ + int num = 0; + + for (const thread_info &thread ATTRIBUTE_UNUSED : l) + ++num; + + return num; } /* See gdbthread.h. */ void -thread_step_over_chain_enqueue (struct thread_info *tp) +global_thread_step_over_chain_enqueue (struct thread_info *tp) { - step_over_chain_enqueue (&step_over_queue_head, tp); + infrun_debug_printf ("enqueueing thread %s in global step over chain", + tp->ptid.to_string ().c_str ()); + + gdb_assert (!thread_is_in_step_over_chain (tp)); + global_thread_step_over_list.push_back (*tp); } /* See gdbthread.h. */ void -thread_step_over_chain_remove (struct thread_info *tp) +global_thread_step_over_chain_enqueue_chain (thread_step_over_list &&list) { - step_over_chain_remove (&step_over_queue_head, tp); + global_thread_step_over_list.splice (std::move (list)); +} + +/* See gdbthread.h. */ + +void +global_thread_step_over_chain_remove (struct thread_info *tp) +{ + infrun_debug_printf ("removing thread %s from global step over chain", + tp->ptid.to_string ().c_str ()); + + gdb_assert (thread_is_in_step_over_chain (tp)); + auto it = global_thread_step_over_list.iterator_to (*tp); + global_thread_step_over_list.erase (it); } -/* Delete the thread referenced by THR. If SILENT, don't notifyi +/* Delete the thread referenced by THR. If SILENT, don't notify the observer of this exit. THR must not be NULL or a failed assertion will be raised. */ @@ -452,35 +460,24 @@ delete_thread_1 (thread_info *thr, bool silent) { gdb_assert (thr != nullptr); - struct thread_info *tp, *tpprev = NULL; + threads_debug_printf ("deleting thread %s, silent = %d", + thr->ptid.to_string ().c_str (), silent); - for (tp = thr->inf->thread_list; tp; tpprev = tp, tp = tp->next) - if (tp == thr) - break; + set_thread_exited (thr, silent); - if (!tp) - return; - - set_thread_exited (tp, silent); - - if (!tp->deletable ()) + if (!thr->deletable ()) { /* Will be really deleted some other time. */ return; } - if (tpprev) - tpprev->next = tp->next; - else - tp->inf->thread_list = tp->next; + auto it = thr->inf->thread_list.iterator_to (*thr); + thr->inf->thread_list.erase (it); - delete tp; + delete thr; } -/* Delete thread THREAD and notify of thread exit. If this is the - current thread, don't actually delete it, but tag it as exited and - do the notification. If this is the user selected thread, clear - it. */ +/* See gdbthread.h. */ void delete_thread (thread_info *thread) @@ -514,12 +511,12 @@ find_thread_id (struct inferior *inf, int thr_num) return NULL; } -/* Find a thread_info by matching PTID. */ +/* See gdbthread.h. */ struct thread_info * -find_thread_ptid (ptid_t ptid) +find_thread_ptid (process_stratum_target *targ, ptid_t ptid) { - inferior *inf = find_inferior_ptid (ptid); + inferior *inf = find_inferior_ptid (targ, ptid); if (inf == NULL) return NULL; return find_thread_ptid (inf, ptid); @@ -530,22 +527,24 @@ find_thread_ptid (ptid_t ptid) struct thread_info * find_thread_ptid (inferior *inf, ptid_t ptid) { - for (thread_info *tp : inf->threads ()) - if (tp->ptid == ptid) - return tp; + gdb_assert (inf != nullptr); - return NULL; + auto it = inf->ptid_thread_map.find (ptid); + if (it != inf->ptid_thread_map.end ()) + return it->second; + else + return nullptr; } /* See gdbthread.h. */ struct thread_info * -find_thread_by_handle (struct value *thread_handle, struct inferior *inf) +find_thread_by_handle (gdb::array_view handle, + struct inferior *inf) { - return target_thread_handle_to_thread_info - (value_contents_all (thread_handle), - TYPE_LENGTH (value_type (thread_handle)), - inf); + return target_thread_handle_to_thread_info (handle.data (), + handle.size (), + inf); } /* @@ -584,9 +583,9 @@ any_thread_p () } int -thread_count (void) +thread_count (process_stratum_target *proc_target) { - auto rng = all_threads (); + auto rng = all_threads (proc_target); return std::distance (rng.begin (), rng.end ()); } @@ -609,10 +608,10 @@ valid_global_thread_id (int global_id) return 0; } -int -in_thread_list (ptid_t ptid) +bool +in_thread_list (process_stratum_target *targ, ptid_t ptid) { - return find_thread_ptid (ptid) != nullptr; + return find_thread_ptid (targ, ptid) != nullptr; } /* Finds the first thread of the inferior. */ @@ -620,7 +619,10 @@ in_thread_list (ptid_t ptid) thread_info * first_thread_of_inferior (inferior *inf) { - return inf->thread_list; + if (inf->thread_list.empty ()) + return nullptr; + + return &inf->thread_list.front (); } thread_info * @@ -628,8 +630,8 @@ any_thread_of_inferior (inferior *inf) { gdb_assert (inf->pid != 0); - /* Prefer the current thread. */ - if (inf == current_inferior ()) + /* Prefer the current thread, if there's one. */ + if (inf == current_inferior () && inferior_ptid != null_ptid) return inferior_thread (); for (thread_info *tp : inf->non_exited_threads ()) @@ -655,13 +657,13 @@ any_live_thread_of_inferior (inferior *inf) curr_tp = inferior_thread (); if (curr_tp->state == THREAD_EXITED) curr_tp = NULL; - else if (!curr_tp->executing) + else if (!curr_tp->executing ()) return curr_tp; } for (thread_info *tp : inf->non_exited_threads ()) { - if (!tp->executing) + if (!tp->executing ()) return tp; tp_executing = tp; @@ -677,14 +679,37 @@ any_live_thread_of_inferior (inferior *inf) } /* Return true if TP is an active thread. */ -static int -thread_alive (struct thread_info *tp) +static bool +thread_alive (thread_info *tp) { if (tp->state == THREAD_EXITED) - return 0; - if (!target_thread_alive (tp->ptid)) - return 0; - return 1; + return false; + + /* Ensure we're looking at the right target stack. */ + gdb_assert (tp->inf == current_inferior ()); + + return target_thread_alive (tp->ptid); +} + +/* See gdbthreads.h. */ + +bool +switch_to_thread_if_alive (thread_info *thr) +{ + scoped_restore_current_thread restore_thread; + + /* Switch inferior first, so that we're looking at the right target + stack. */ + switch_to_inferior_no_thread (thr->inf); + + if (thread_alive (thr)) + { + switch_to_thread (thr); + restore_thread.dont_restore (); + return true; + } + + return false; } /* See gdbthreads.h. */ @@ -692,9 +717,15 @@ thread_alive (struct thread_info *tp) void prune_threads (void) { + scoped_restore_current_thread restore_thread; + for (thread_info *tp : all_threads_safe ()) - if (!thread_alive (tp)) - delete_thread (tp); + { + switch_to_inferior_no_thread (tp->inf); + + if (!thread_alive (tp)) + delete_thread (tp); + } } /* See gdbthreads.h. */ @@ -707,7 +738,7 @@ delete_exited_threads (void) delete_thread (tp); } -/* Return true value if stack temporaies are enabled for the thread +/* Return true value if stack temporaries are enabled for the thread TP. */ bool @@ -758,7 +789,8 @@ get_last_thread_stack_temporary (thread_info *tp) } void -thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid) +thread_change_ptid (process_stratum_target *targ, + ptid_t old_ptid, ptid_t new_ptid) { struct inferior *inf; struct thread_info *tp; @@ -766,34 +798,40 @@ thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid) /* It can happen that what we knew as the target inferior id changes. E.g, target remote may only discover the remote process pid after adding the inferior to GDB's list. */ - inf = find_inferior_ptid (old_ptid); + inf = find_inferior_ptid (targ, old_ptid); inf->pid = new_ptid.pid (); tp = find_thread_ptid (inf, old_ptid); + gdb_assert (tp != nullptr); + + int num_erased = inf->ptid_thread_map.erase (old_ptid); + gdb_assert (num_erased == 1); + tp->ptid = new_ptid; + inf->ptid_thread_map[new_ptid] = tp; - gdb::observers::thread_ptid_changed.notify (old_ptid, new_ptid); + gdb::observers::thread_ptid_changed.notify (targ, old_ptid, new_ptid); } /* See gdbthread.h. */ void -set_resumed (ptid_t ptid, int resumed) +set_resumed (process_stratum_target *targ, ptid_t ptid, bool resumed) { - for (thread_info *tp : all_non_exited_threads (ptid)) - tp->resumed = resumed; + for (thread_info *tp : all_non_exited_threads (targ, ptid)) + tp->set_resumed (resumed); } /* Helper for set_running, that marks one thread either running or stopped. */ -static int -set_running_thread (struct thread_info *tp, int running) +static bool +set_running_thread (struct thread_info *tp, bool running) { - int started = 0; + bool started = false; if (running && tp->state == THREAD_STOPPED) - started = 1; + started = true; tp->state = running ? THREAD_RUNNING : THREAD_STOPPED; if (!running) @@ -801,8 +839,8 @@ set_running_thread (struct thread_info *tp, int running) /* If the thread is now marked stopped, remove it from the step-over queue, so that we don't try to resume it until the user wants it to. */ - if (tp->step_over_next != NULL) - thread_step_over_chain_remove (tp); + if (thread_is_in_step_over_chain (tp)) + global_thread_step_over_chain_remove (tp); } return started; @@ -818,7 +856,7 @@ thread_info::set_running (bool running) } void -set_running (ptid_t ptid, int running) +set_running (process_stratum_target *targ, ptid_t ptid, bool running) { /* We try not to notify the observer if no thread has actually changed the running state -- merely to reduce the number of @@ -826,7 +864,7 @@ set_running (ptid_t ptid, int running) multiple *running notifications just fine. */ bool any_started = false; - for (thread_info *tp : all_non_exited_threads (ptid)) + for (thread_info *tp : all_non_exited_threads (targ, ptid)) if (set_running_thread (tp, running)) any_started = true; @@ -834,46 +872,33 @@ set_running (ptid_t ptid, int running) gdb::observers::target_resumed.notify (ptid); } - -/* Helper for set_executing. Set's the thread's 'executing' field - from EXECUTING, and if EXECUTING is true also clears the thread's - stop_pc. */ - -static void -set_executing_thread (thread_info *thr, bool executing) -{ - thr->executing = executing; - if (executing) - thr->suspend.stop_pc = ~(CORE_ADDR) 0; -} - void -set_executing (ptid_t ptid, int executing) +set_executing (process_stratum_target *targ, ptid_t ptid, bool executing) { - for (thread_info *tp : all_non_exited_threads (ptid)) - set_executing_thread (tp, executing); + for (thread_info *tp : all_non_exited_threads (targ, ptid)) + tp->set_executing (executing); /* It only takes one running thread to spawn more threads. */ if (executing) - threads_executing = 1; + targ->threads_executing = true; /* Only clear the flag if the caller is telling us everything is stopped. */ else if (minus_one_ptid == ptid) - threads_executing = 0; + targ->threads_executing = false; } /* See gdbthread.h. */ -int -threads_are_executing (void) +bool +threads_are_executing (process_stratum_target *target) { - return threads_executing; + return target->threads_executing; } void -set_stop_requested (ptid_t ptid, int stop) +set_stop_requested (process_stratum_target *targ, ptid_t ptid, bool stop) { - for (thread_info *tp : all_non_exited_threads (ptid)) + for (thread_info *tp : all_non_exited_threads (targ, ptid)) tp->stop_requested = stop; /* Call the stop requested observer so other components of GDB can @@ -883,12 +908,12 @@ set_stop_requested (ptid_t ptid, int stop) } void -finish_thread_state (ptid_t ptid) +finish_thread_state (process_stratum_target *targ, ptid_t ptid) { bool any_started = false; - for (thread_info *tp : all_non_exited_threads (ptid)) - if (set_running_thread (tp, tp->executing)) + for (thread_info *tp : all_non_exited_threads (targ, ptid)) + if (set_running_thread (tp, tp->executing ())) any_started = true; if (any_started) @@ -915,7 +940,7 @@ validate_registers_access (void) at the prompt) when a thread is not executing for some internal reason, but is marked running from the user's perspective. E.g., the thread is waiting for its turn in the step-over queue. */ - if (tp->executing) + if (tp->executing ()) error (_("Selected thread is running.")); } @@ -933,7 +958,7 @@ can_access_registers_thread (thread_info *thread) return false; /* ... or from a spinning thread. FIXME: see validate_registers_access. */ - if (thread->executing) + if (thread->executing ()) return false; return true; @@ -994,7 +1019,7 @@ thread_target_id_str (thread_info *tp) { std::string target_id = target_pid_to_str (tp->ptid); const char *extra_info = target_extra_thread_info (tp); - const char *name = tp->name != nullptr ? tp->name : target_thread_name (tp); + const char *name = thread_name (tp); if (extra_info != nullptr && name != nullptr) return string_printf ("%s \"%s\" (%s)", target_id.c_str (), name, @@ -1035,6 +1060,9 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, gdb::optional list_emitter; gdb::optional table_emitter; + /* We'll be switching threads temporarily below. */ + scoped_restore_current_thread restore_thread; + if (uiout->is_mi_like_p ()) list_emitter.emplace (uiout, "threads"); else @@ -1052,6 +1080,10 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, if (!uiout->is_mi_like_p ()) { + /* Switch inferiors so we're looking at the right + target stack. */ + switch_to_inferior_no_thread (tp->inf); + target_id_col_width = std::max (target_id_col_width, thread_target_id_str (tp).size ()); @@ -1083,90 +1115,86 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, uiout->table_body (); } - /* We'll be switching threads temporarily. */ - scoped_restore_current_thread restore_thread; - for (inferior *inf : all_inferiors ()) for (thread_info *tp : inf->threads ()) - { - int core; + { + int core; - any_thread = true; - if (tp == current_thread && tp->state == THREAD_EXITED) - current_exited = true; + any_thread = true; + if (tp == current_thread && tp->state == THREAD_EXITED) + current_exited = true; - if (!should_print_thread (requested_threads, default_inf_num, - global_ids, pid, tp)) - continue; + if (!should_print_thread (requested_threads, default_inf_num, + global_ids, pid, tp)) + continue; - ui_out_emit_tuple tuple_emitter (uiout, NULL); + ui_out_emit_tuple tuple_emitter (uiout, NULL); - if (!uiout->is_mi_like_p ()) - { - if (tp == current_thread) - uiout->field_string ("current", "*"); - else - uiout->field_skip ("current"); + if (!uiout->is_mi_like_p ()) + { + if (tp == current_thread) + uiout->field_string ("current", "*"); + else + uiout->field_skip ("current"); - uiout->field_string ("id-in-tg", print_thread_id (tp)); - } + uiout->field_string ("id-in-tg", print_thread_id (tp)); + } - if (show_global_ids || uiout->is_mi_like_p ()) - uiout->field_int ("id", tp->global_num); + if (show_global_ids || uiout->is_mi_like_p ()) + uiout->field_signed ("id", tp->global_num); - /* For the CLI, we stuff everything into the target-id field. - This is a gross hack to make the output come out looking - correct. The underlying problem here is that ui-out has no - way to specify that a field's space allocation should be - shared by several fields. For MI, we do the right thing - instead. */ + /* Switch to the thread (and inferior / target). */ + switch_to_thread (tp); - if (uiout->is_mi_like_p ()) - { - uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); + /* For the CLI, we stuff everything into the target-id field. + This is a gross hack to make the output come out looking + correct. The underlying problem here is that ui-out has no + way to specify that a field's space allocation should be + shared by several fields. For MI, we do the right thing + instead. */ - const char *extra_info = target_extra_thread_info (tp); - if (extra_info != nullptr) - uiout->field_string ("details", extra_info); + if (uiout->is_mi_like_p ()) + { + uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); - const char *name = (tp->name != nullptr - ? tp->name - : target_thread_name (tp)); - if (name != NULL) - uiout->field_string ("name", name); - } - else - { - uiout->field_string ("target-id", - thread_target_id_str (tp).c_str ()); - } + const char *extra_info = target_extra_thread_info (tp); + if (extra_info != nullptr) + uiout->field_string ("details", extra_info); - if (tp->state == THREAD_RUNNING) - uiout->text ("(running)\n"); - else - { - /* The switch below puts us at the top of the stack (leaf - frame). */ - switch_to_thread (tp); - print_stack_frame (get_selected_frame (NULL), - /* For MI output, print frame level. */ - uiout->is_mi_like_p (), - LOCATION, 0); - } + const char *name = thread_name (tp); + if (name != NULL) + uiout->field_string ("name", name); + } + else + { + uiout->field_string ("target-id", thread_target_id_str (tp)); + } - if (uiout->is_mi_like_p ()) - { - const char *state = "stopped"; + if (tp->state == THREAD_RUNNING) + uiout->text ("(running)\n"); + else + { + /* The switch above put us at the top of the stack (leaf + frame). */ + print_stack_frame (get_selected_frame (NULL), + /* For MI output, print frame level. */ + uiout->is_mi_like_p (), + LOCATION, 0); + } - if (tp->state == THREAD_RUNNING) - state = "running"; - uiout->field_string ("state", state); - } + if (uiout->is_mi_like_p ()) + { + const char *state = "stopped"; - core = target_core_of_thread (tp->ptid); - if (uiout->is_mi_like_p () && core != -1) - uiout->field_int ("core", core); - } + if (tp->state == THREAD_RUNNING) + state = "running"; + uiout->field_string ("state", state); + } + + core = target_core_of_thread (tp->ptid); + if (uiout->is_mi_like_p () && core != -1) + uiout->field_signed ("core", core); + } /* This end scope restores the current thread and the frame selected before the "info threads" command, and it finishes the @@ -1176,7 +1204,7 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, if (pid == -1 && requested_threads == NULL) { if (uiout->is_mi_like_p () && inferior_ptid != null_ptid) - uiout->field_int ("current-thread-id", current_thread->global_num); + uiout->field_signed ("current-thread-id", current_thread->global_num); if (inferior_ptid != null_ptid && current_exited) uiout->message ("\n\ @@ -1191,11 +1219,39 @@ No selected thread. See `help thread'.\n"); /* See gdbthread.h. */ void -print_thread_info (struct ui_out *uiout, char *requested_threads, int pid) +print_thread_info (struct ui_out *uiout, const char *requested_threads, + int pid) { print_thread_info_1 (uiout, requested_threads, 1, pid, 0); } +/* The options for the "info threads" command. */ + +struct info_threads_opts +{ + /* For "-gid". */ + bool show_global_ids = false; +}; + +static const gdb::option::option_def info_threads_option_defs[] = { + + gdb::option::flag_option_def { + "gid", + [] (info_threads_opts *opts) { return &opts->show_global_ids; }, + N_("Show global thread IDs."), + }, + +}; + +/* Create an option_def_group for the "info threads" options, with + IT_OPTS as context. */ + +static inline gdb::option::option_def_group +make_info_threads_options_def_group (info_threads_opts *it_opts) +{ + return {{info_threads_option_defs}, it_opts}; +} + /* Implementation of the "info threads" command. Note: this has the drawback that it _really_ switches @@ -1205,16 +1261,36 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid) static void info_threads_command (const char *arg, int from_tty) { - int show_global_ids = 0; + info_threads_opts it_opts; + + auto grp = make_info_threads_options_def_group (&it_opts); + gdb::option::process_options + (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp); + + print_thread_info_1 (current_uiout, arg, 0, -1, it_opts.show_global_ids); +} + +/* Completer for the "info threads" command. */ + +static void +info_threads_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word_ignored) +{ + const auto grp = make_info_threads_options_def_group (nullptr); + + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp)) + return; - if (arg != NULL - && check_for_argument (&arg, "-gid", sizeof ("-gid") - 1)) + /* Convenience to let the user know what the option can accept. */ + if (*text == '\0') { - arg = skip_spaces (arg); - show_global_ids = 1; + gdb::option::complete_on_all_options (tracker, grp); + /* Keep this "ID" in sync with what "help info threads" + says. */ + tracker.add_completion (make_unique_xstrdup ("ID")); } - - print_thread_info_1 (current_uiout, arg, 0, -1, show_global_ids); } /* See gdbthread.h. */ @@ -1227,7 +1303,8 @@ switch_to_thread_no_regs (struct thread_info *thread) set_current_program_space (inf->pspace); set_current_inferior (inf); - inferior_ptid = thread->ptid; + current_thread_ = thread; + inferior_ptid = current_thread_->ptid; } /* See gdbthread.h. */ @@ -1235,9 +1312,10 @@ switch_to_thread_no_regs (struct thread_info *thread) void switch_to_no_thread () { - if (inferior_ptid == null_ptid) + if (current_thread_ == nullptr) return; + current_thread_ = nullptr; inferior_ptid = null_ptid; reinit_frame_cache (); } @@ -1249,7 +1327,7 @@ switch_to_thread (thread_info *thr) { gdb_assert (thr != NULL); - if (inferior_ptid == thr->ptid) + if (is_current_thread (thr)) return; switch_to_thread_no_regs (thr); @@ -1257,75 +1335,19 @@ switch_to_thread (thread_info *thr) reinit_frame_cache (); } -/* See common/common-gdbthread.h. */ +/* See gdbsupport/common-gdbthread.h. */ void -switch_to_thread (ptid_t ptid) +switch_to_thread (process_stratum_target *proc_target, ptid_t ptid) { - thread_info *thr = find_thread_ptid (ptid); + thread_info *thr = find_thread_ptid (proc_target, ptid); switch_to_thread (thr); } -static void -restore_selected_frame (struct frame_id a_frame_id, int frame_level) -{ - struct frame_info *frame = NULL; - int count; +/* See frame.h. */ - /* This means there was no selected frame. */ - if (frame_level == -1) - { - select_frame (NULL); - return; - } - - gdb_assert (frame_level >= 0); - - /* Restore by level first, check if the frame id is the same as - expected. If that fails, try restoring by frame id. If that - fails, nothing to do, just warn the user. */ - - count = frame_level; - frame = find_relative_frame (get_current_frame (), &count); - if (count == 0 - && frame != NULL - /* The frame ids must match - either both valid or both outer_frame_id. - The latter case is not failsafe, but since it's highly unlikely - the search by level finds the wrong frame, it's 99.9(9)% of - the time (for all practical purposes) safe. */ - && frame_id_eq (get_frame_id (frame), a_frame_id)) - { - /* Cool, all is fine. */ - select_frame (frame); - return; - } - - frame = frame_find_by_id (a_frame_id); - if (frame != NULL) - { - /* Cool, refound it. */ - select_frame (frame); - return; - } - - /* Nothing else to do, the frame layout really changed. Select the - innermost stack frame. */ - select_frame (get_current_frame ()); - - /* Warn the user. */ - if (frame_level > 0 && !current_uiout->is_mi_like_p ()) - { - warning (_("Couldn't restore frame #%d in " - "current thread. Bottom (innermost) frame selected:"), - frame_level); - /* For MI, we should probably have a notification about - current frame change. But this error is not very - likely, so don't bother for now. */ - print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1); - } -} - -scoped_restore_current_thread::~scoped_restore_current_thread () +void +scoped_restore_current_thread::restore () { /* If an entry of thread_info was previously selected, it won't be deleted because we've increased its refcount. The thread represented @@ -1336,62 +1358,42 @@ scoped_restore_current_thread::~scoped_restore_current_thread () in the mean time exited (or killed, detached, etc.), then don't revert back to it, but instead simply drop back to no thread selected. */ && m_inf->pid != 0) - switch_to_thread (m_thread); + switch_to_thread (m_thread.get ()); else - { - switch_to_no_thread (); - set_current_inferior (m_inf); - } + switch_to_inferior_no_thread (m_inf.get ()); /* The running state of the originally selected thread may have changed, so we have to recheck it here. */ if (inferior_ptid != null_ptid && m_was_stopped && m_thread->state == THREAD_STOPPED - && target_has_registers - && target_has_stack - && target_has_memory) + && target_has_registers () + && target_has_stack () + && target_has_memory ()) restore_selected_frame (m_selected_frame_id, m_selected_frame_level); - if (m_thread != NULL) - m_thread->decref (); - m_inf->decref (); + set_language (m_lang); +} + +scoped_restore_current_thread::~scoped_restore_current_thread () +{ + if (!m_dont_restore) + restore (); } scoped_restore_current_thread::scoped_restore_current_thread () { - m_thread = NULL; - m_inf = current_inferior (); + m_inf = inferior_ref::new_reference (current_inferior ()); + + m_lang = current_language->la_language; if (inferior_ptid != null_ptid) { - thread_info *tp = inferior_thread (); - struct frame_info *frame; - - m_was_stopped = tp->state == THREAD_STOPPED; - if (m_was_stopped - && target_has_registers - && target_has_stack - && target_has_memory) - { - /* When processing internal events, there might not be a - selected frame. If we naively call get_selected_frame - here, then we can end up reading debuginfo for the - current frame, but we don't generally need the debuginfo - at this point. */ - frame = get_selected_frame_if_set (); - } - else - frame = NULL; + m_thread = thread_info_ref::new_reference (inferior_thread ()); - m_selected_frame_id = get_frame_id (frame); - m_selected_frame_level = frame_relative_level (frame); - - tp->incref (); - m_thread = tp; + m_was_stopped = m_thread->state == THREAD_STOPPED; + save_selected_frame (&m_selected_frame_id, &m_selected_frame_level); } - - m_inf->incref (); } /* See gdbthread.h. */ @@ -1407,7 +1409,11 @@ show_thread_that_caused_stop (void) int show_inferior_qualified_tids (void) { - return (inferior_list->next != NULL || inferior_list->num != 1); + auto inf = inferior_list.begin (); + if (inf->num != 1) + return true; + ++inf; + return inf != inferior_list.end (); } /* See gdbthread.h. */ @@ -1424,69 +1430,132 @@ print_thread_id (struct thread_info *thr) return s; } -/* If true, tp_array_compar should sort in ascending order, otherwise - in descending order. */ +/* Sort an array of struct thread_info pointers by thread ID (first by + inferior number, and then by per-inferior thread number). Sorts in + ascending order. */ + +static bool +tp_array_compar_ascending (const thread_info_ref &a, const thread_info_ref &b) +{ + if (a->inf->num != b->inf->num) + return a->inf->num < b->inf->num; -static bool tp_array_compar_ascending; + return (a->per_inf_num < b->per_inf_num); +} -/* Sort an array for struct thread_info pointers by thread ID (first - by inferior number, and then by per-inferior thread number). The - order is determined by TP_ARRAY_COMPAR_ASCENDING. */ +/* Sort an array of struct thread_info pointers by thread ID (first by + inferior number, and then by per-inferior thread number). Sorts in + descending order. */ static bool -tp_array_compar (const thread_info *a, const thread_info *b) +tp_array_compar_descending (const thread_info_ref &a, const thread_info_ref &b) { if (a->inf->num != b->inf->num) - { - if (tp_array_compar_ascending) - return a->inf->num < b->inf->num; - else - return a->inf->num > b->inf->num; - } + return a->inf->num > b->inf->num; - if (tp_array_compar_ascending) - return (a->per_inf_num < b->per_inf_num); - else - return (a->per_inf_num > b->per_inf_num); + return (a->per_inf_num > b->per_inf_num); } -/* Switch to thread THR and execute CMD. - FLAGS.QUIET controls the printing of the thread information. - FLAGS.CONT and FLAGS.SILENT control how to handle errors. */ +/* See gdbthread.h. */ + +void +thread_try_catch_cmd (thread_info *thr, gdb::optional ada_task, + const char *cmd, int from_tty, + const qcs_flags &flags) +{ + gdb_assert (is_current_thread (thr)); + + /* The thread header is computed before running the command since + the command can change the inferior, which is not permitted + by thread_target_id_str. */ + std::string thr_header; + if (ada_task.has_value ()) + thr_header = string_printf (_("\nTask ID %d:\n"), *ada_task); + else + thr_header = string_printf (_("\nThread %s (%s):\n"), + print_thread_id (thr), + thread_target_id_str (thr).c_str ()); -static void -thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty, - const qcs_flags &flags) -{ - switch_to_thread (thr); try { - std::string cmd_result = execute_command_to_string (cmd, from_tty); + std::string cmd_result; + execute_command_to_string + (cmd_result, cmd, from_tty, gdb_stdout->term_out ()); if (!flags.silent || cmd_result.length () > 0) { if (!flags.quiet) - printf_filtered (_("\nThread %s (%s):\n"), - print_thread_id (thr), - target_pid_to_str (inferior_ptid).c_str ()); - printf_filtered ("%s", cmd_result.c_str ()); + gdb_printf ("%s", thr_header.c_str ()); + gdb_printf ("%s", cmd_result.c_str ()); } } - catch (const gdb_exception_RETURN_MASK_ERROR &ex) + catch (const gdb_exception_error &ex) { if (!flags.silent) { if (!flags.quiet) - printf_filtered (_("\nThread %s (%s):\n"), - print_thread_id (thr), - target_pid_to_str (inferior_ptid).c_str ()); + gdb_printf ("%s", thr_header.c_str ()); if (flags.cont) - printf_filtered ("%s\n", ex.what ()); + gdb_printf ("%s\n", ex.what ()); else - throw_exception (ex); + throw; } } } +/* Option definition of "thread apply"'s "-ascending" option. */ + +static const gdb::option::flag_option_def<> ascending_option_def = { + "ascending", + N_("\ +Call COMMAND for all threads in ascending order.\n\ +The default is descending order."), +}; + +/* The qcs command line flags for the "thread apply" commands. Keep + this in sync with the "frame apply" commands. */ + +using qcs_flag_option_def + = gdb::option::flag_option_def; + +static const gdb::option::option_def thr_qcs_flags_option_defs[] = { + qcs_flag_option_def { + "q", [] (qcs_flags *opt) { return &opt->quiet; }, + N_("Disables printing the thread information."), + }, + + qcs_flag_option_def { + "c", [] (qcs_flags *opt) { return &opt->cont; }, + N_("Print any error raised by COMMAND and continue."), + }, + + qcs_flag_option_def { + "s", [] (qcs_flags *opt) { return &opt->silent; }, + N_("Silently ignore any errors or empty output produced by COMMAND."), + }, +}; + +/* Create an option_def_group for the "thread apply all" options, with + ASCENDING and FLAGS as context. */ + +static inline std::array +make_thread_apply_all_options_def_group (bool *ascending, + qcs_flags *flags) +{ + return {{ + { {ascending_option_def.def ()}, ascending}, + { {thr_qcs_flags_option_defs}, flags }, + }}; +} + +/* Create an option_def_group for the "thread apply" options, with + FLAGS as context. */ + +static inline gdb::option::option_def_group +make_thread_apply_options_def_group (qcs_flags *flags) +{ + return {{thr_qcs_flags_option_defs}, flags}; +} + /* Apply a GDB command to a list of threads. List syntax is a whitespace separated list of numbers, or ranges, or the keyword `all'. Ranges consist of two numbers separated by a hyphen. Examples: @@ -1498,24 +1567,15 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty, static void thread_apply_all_command (const char *cmd, int from_tty) { + bool ascending = false; qcs_flags flags; - tp_array_compar_ascending = false; - - while (cmd != NULL) - { - if (check_for_argument (&cmd, "-ascending", strlen ("-ascending"))) - { - cmd = skip_spaces (cmd); - tp_array_compar_ascending = true; - continue; - } - - if (parse_flags_qcs ("thread apply all", &cmd, &flags)) - continue; + auto group = make_thread_apply_all_options_def_group (&ascending, + &flags); + gdb::option::process_options + (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group); - break; - } + validate_flags_qcs ("thread apply all", &flags); if (cmd == NULL || *cmd == '\000') error (_("Please specify a command at the end of 'thread apply all'")); @@ -1530,27 +1590,101 @@ thread_apply_all_command (const char *cmd, int from_tty) thread, in case the command is one that wipes threads. E.g., detach, kill, disconnect, etc., or even normally continuing over an inferior or thread exit. */ - std::vector thr_list_cpy; + std::vector thr_list_cpy; thr_list_cpy.reserve (tc); for (thread_info *tp : all_non_exited_threads ()) - thr_list_cpy.push_back (tp); + thr_list_cpy.push_back (thread_info_ref::new_reference (tp)); gdb_assert (thr_list_cpy.size () == tc); - /* Increment the refcounts, and restore them back on scope - exit. */ - scoped_inc_dec_ref inc_dec_ref (thr_list_cpy); - - std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), tp_array_compar); + auto *sorter = (ascending + ? tp_array_compar_ascending + : tp_array_compar_descending); + std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), sorter); scoped_restore_current_thread restore_thread; - for (thread_info *thr : thr_list_cpy) - if (thread_alive (thr)) - thr_try_catch_cmd (thr, cmd, from_tty, flags); + for (thread_info_ref &thr : thr_list_cpy) + if (switch_to_thread_if_alive (thr.get ())) + thread_try_catch_cmd (thr.get (), {}, cmd, from_tty, flags); } } +/* Completer for "thread apply [ID list]". */ + +static void +thread_apply_command_completer (cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char * /*word*/) +{ + /* Don't leave this to complete_options because there's an early + return below. */ + tracker.set_use_custom_word_point (true); + + tid_range_parser parser; + parser.init (text, current_inferior ()->num); + + try + { + while (!parser.finished ()) + { + int inf_num, thr_start, thr_end; + + if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end)) + break; + + if (parser.in_star_range () || parser.in_thread_range ()) + parser.skip_range (); + } + } + catch (const gdb_exception_error &ex) + { + /* get_tid_range throws if it parses a negative number, for + example. But a seemingly negative number may be the start of + an option instead. */ + } + + const char *cmd = parser.cur_tok (); + + if (cmd == text) + { + /* No thread ID list yet. */ + return; + } + + /* Check if we're past a valid thread ID list already. */ + if (parser.finished () + && cmd > text && !isspace (cmd[-1])) + return; + + /* We're past the thread ID list, advance word point. */ + tracker.advance_custom_word_point_by (cmd - text); + text = cmd; + + const auto group = make_thread_apply_options_def_group (nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + complete_nested_command_line (tracker, text); +} + +/* Completer for "thread apply all". */ + +static void +thread_apply_all_command_completer (cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + const auto group = make_thread_apply_all_options_def_group (nullptr, + nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + complete_nested_command_line (tracker, text); +} + /* Implementation of the "thread apply" command. */ static void @@ -1558,7 +1692,6 @@ thread_apply_command (const char *tidlist, int from_tty) { qcs_flags flags; const char *cmd = NULL; - const char *cmd_or_flags; tid_range_parser parser; if (tidlist == NULL || *tidlist == '\000') @@ -1570,26 +1703,27 @@ thread_apply_command (const char *tidlist, int from_tty) int inf_num, thr_start, thr_end; if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end)) - { - cmd = parser.cur_tok (); - break; - } + break; } - cmd_or_flags = cmd; - while (cmd != NULL && parse_flags_qcs ("thread apply", &cmd, &flags)) - ; + cmd = parser.cur_tok (); + + auto group = make_thread_apply_options_def_group (&flags); + gdb::option::process_options + (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group); - if (cmd == NULL) + validate_flags_qcs ("thread apply", &flags); + + if (*cmd == '\0') error (_("Please specify a command following the thread ID list")); - if (tidlist == cmd || !isalpha (cmd[0])) + if (tidlist == cmd || isdigit (cmd[0])) invalid_thread_id_error (cmd); scoped_restore_current_thread restore_thread; parser.init (tidlist, current_inferior ()->num); - while (!parser.finished () && parser.cur_tok () < cmd_or_flags) + while (!parser.finished ()) { struct thread_info *tp = NULL; struct inferior *inf; @@ -1628,13 +1762,13 @@ thread_apply_command (const char *tidlist, int from_tty) continue; } - if (!thread_alive (tp)) + if (!switch_to_thread_if_alive (tp)) { warning (_("Thread %s has terminated."), print_thread_id (tp)); continue; } - thr_try_catch_cmd (tp, cmd, from_tty, flags); + thread_try_catch_cmd (tp, {}, cmd, from_tty, flags); } } @@ -1644,6 +1778,8 @@ thread_apply_command (const char *tidlist, int from_tty) static void taas_command (const char *cmd, int from_tty) { + if (cmd == NULL || *cmd == '\0') + error (_("Please specify a command to apply on all threads")); std::string expanded = std::string ("thread apply all -s ") + cmd; execute_command (expanded.c_str (), from_tty); } @@ -1653,8 +1789,10 @@ taas_command (const char *cmd, int from_tty) static void tfaas_command (const char *cmd, int from_tty) { + if (cmd == NULL || *cmd == '\0') + error (_("Please specify a command to apply on all frames of all threads")); std::string expanded - = std::string ("thread apply all -s frame apply all -s ") + cmd; + = std::string ("thread apply all -s -- frame apply all -s ") + cmd; execute_command (expanded.c_str (), from_tty); } @@ -1668,18 +1806,18 @@ thread_command (const char *tidstr, int from_tty) if (inferior_ptid == null_ptid) error (_("No thread selected")); - if (target_has_stack) + if (target_has_stack ()) { struct thread_info *tp = inferior_thread (); if (tp->state == THREAD_EXITED) - printf_filtered (_("[Current thread is %s (%s) (exited)]\n"), - print_thread_id (tp), - target_pid_to_str (inferior_ptid).c_str ()); + gdb_printf (_("[Current thread is %s (%s) (exited)]\n"), + print_thread_id (tp), + target_pid_to_str (inferior_ptid).c_str ()); else - printf_filtered (_("[Current thread is %s (%s)]\n"), - print_thread_id (tp), - target_pid_to_str (inferior_ptid).c_str ()); + gdb_printf (_("[Current thread is %s (%s)]\n"), + print_thread_id (tp), + target_pid_to_str (inferior_ptid).c_str ()); } else error (_("No stack.")); @@ -1719,8 +1857,7 @@ thread_name_command (const char *arg, int from_tty) arg = skip_spaces (arg); info = inferior_thread (); - xfree (info->name); - info->name = arg ? xstrdup (arg) : NULL; + info->set_name (arg != nullptr ? make_unique_xstrdup (arg) : nullptr); } /* Find thread ids with a name, target pid, or extra info matching ARG. */ @@ -1738,53 +1875,59 @@ thread_find_command (const char *arg, int from_tty) if (tmp != 0) error (_("Invalid regexp (%s): %s"), tmp, arg); + /* We're going to be switching threads. */ + scoped_restore_current_thread restore_thread; + update_thread_list (); + for (thread_info *tp : all_threads ()) { - if (tp->name != NULL && re_exec (tp->name)) + switch_to_inferior_no_thread (tp->inf); + + if (tp->name () != nullptr && re_exec (tp->name ())) { - printf_filtered (_("Thread %s has name '%s'\n"), - print_thread_id (tp), tp->name); + gdb_printf (_("Thread %s has name '%s'\n"), + print_thread_id (tp), tp->name ()); match++; } tmp = target_thread_name (tp); if (tmp != NULL && re_exec (tmp)) { - printf_filtered (_("Thread %s has target name '%s'\n"), - print_thread_id (tp), tmp); + gdb_printf (_("Thread %s has target name '%s'\n"), + print_thread_id (tp), tmp); match++; } std::string name = target_pid_to_str (tp->ptid); if (!name.empty () && re_exec (name.c_str ())) { - printf_filtered (_("Thread %s has target id '%s'\n"), - print_thread_id (tp), name.c_str ()); + gdb_printf (_("Thread %s has target id '%s'\n"), + print_thread_id (tp), name.c_str ()); match++; } tmp = target_extra_thread_info (tp); if (tmp != NULL && re_exec (tmp)) { - printf_filtered (_("Thread %s has extra info '%s'\n"), - print_thread_id (tp), tmp); + gdb_printf (_("Thread %s has extra info '%s'\n"), + print_thread_id (tp), tmp); match++; } } if (!match) - printf_filtered (_("No threads match '%s'\n"), arg); + gdb_printf (_("No threads match '%s'\n"), arg); } /* Print notices when new threads are attached and detached. */ -int print_thread_events = 1; +bool print_thread_events = true; static void show_print_thread_events (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) { - fprintf_filtered (file, - _("Printing of thread events is %s.\n"), - value); + gdb_printf (file, + _("Printing of thread events is %s.\n"), + value); } /* See gdbthread.h. */ @@ -1792,16 +1935,14 @@ show_print_thread_events (struct ui_file *file, int from_tty, void thread_select (const char *tidstr, thread_info *tp) { - if (!thread_alive (tp)) + if (!switch_to_thread_if_alive (tp)) error (_("Thread ID %s has terminated."), tidstr); - switch_to_thread (tp); - annotate_thread_changed (); /* Since the current thread may have changed, see if there is any exited thread we can now delete. */ - prune_threads (); + delete_exited_threads (); } /* Print thread and frame switch command response. */ @@ -1816,15 +1957,15 @@ print_selected_thread_frame (struct ui_out *uiout, { if (uiout->is_mi_like_p ()) { - uiout->field_int ("new-thread-id", - inferior_thread ()->global_num); + uiout->field_signed ("new-thread-id", + inferior_thread ()->global_num); } else { uiout->text ("[Switching to thread "); uiout->field_string ("new-thread-id", print_thread_id (tp)); uiout->text (" ("); - uiout->text (target_pid_to_str (inferior_ptid).c_str ()); + uiout->text (target_pid_to_str (inferior_ptid)); uiout->text (")]"); } } @@ -1846,18 +1987,39 @@ print_selected_thread_frame (struct ui_out *uiout, } /* Update the 'threads_executing' global based on the threads we know - about right now. */ + about right now. This is used by infrun to tell whether we should + pull events out of the current target. */ static void update_threads_executing (void) { - threads_executing = 0; - for (thread_info *tp : all_non_exited_threads ()) + process_stratum_target *targ = current_inferior ()->process_target (); + + if (targ == NULL) + return; + + targ->threads_executing = false; + + for (inferior *inf : all_non_exited_inferiors (targ)) { - if (tp->executing) + if (!inf->has_execution ()) + continue; + + /* If the process has no threads, then it must be we have a + process-exit event pending. */ + if (inf->thread_list.empty ()) + { + targ->threads_executing = true; + return; + } + + for (thread_info *tp : inf->non_exited_threads ()) { - threads_executing = 1; - break; + if (tp->executing ()) + { + targ->threads_executing = true; + return; + } } } } @@ -1869,6 +2031,44 @@ update_thread_list (void) update_threads_executing (); } +/* See gdbthread.h. */ + +const char * +thread_name (thread_info *thread) +{ + /* Use the manually set name if there is one. */ + const char *name = thread->name (); + if (name != nullptr) + return name; + + /* Otherwise, ask the target. Ensure we query the right target stack. */ + scoped_restore_current_thread restore_thread; + if (thread->inf != current_inferior ()) + switch_to_inferior_no_thread (thread->inf); + + return target_thread_name (thread); +} + +/* See gdbthread.h. */ + +const char * +thread_state_string (enum thread_state state) +{ + switch (state) + { + case THREAD_STOPPED: + return "STOPPED"; + + case THREAD_RUNNING: + return "RUNNING"; + + case THREAD_EXITED: + return "EXITED"; + } + + gdb_assert_not_reached ("unknown thread state"); +} + /* Return a new value for the selected thread's id. Return a value of 0 if no thread is selected. If GLOBAL is true, return the thread's global number. Otherwise return the per-inferior number. */ @@ -1923,7 +2123,6 @@ static const struct internalvar_funcs thread_funcs = { thread_id_per_inf_num_make_value, NULL, - NULL }; /* Implementation of `gthread' variable. */ @@ -1932,62 +2131,94 @@ static const struct internalvar_funcs gthread_funcs = { global_thread_id_make_value, NULL, - NULL }; +void _initialize_thread (); void -_initialize_thread (void) +_initialize_thread () { static struct cmd_list_element *thread_apply_list = NULL; + cmd_list_element *c; + + const auto info_threads_opts = make_info_threads_options_def_group (nullptr); - add_info ("threads", info_threads_command, - _("Display currently known threads.\n\ -Usage: info threads [-gid] [ID]...\n\ --gid: Show global thread IDs.\n\ + /* Note: keep this "ID" in sync with what "info threads [TAB]" + suggests. */ + static std::string info_threads_help + = gdb::option::build_help (_("\ +Display currently known threads.\n\ +Usage: info threads [OPTION]... [ID]...\n\ If ID is given, it is a space-separated list of IDs of threads to display.\n\ -Otherwise, all threads are displayed.")); +Otherwise, all threads are displayed.\n\ +\n\ +Options:\n\ +%OPTIONS%"), + info_threads_opts); - add_prefix_cmd ("thread", class_run, thread_command, _("\ + c = add_info ("threads", info_threads_command, info_threads_help.c_str ()); + set_cmd_completer_handle_brkchars (c, info_threads_command_completer); + + cmd_list_element *thread_cmd + = add_prefix_cmd ("thread", class_run, thread_command, _("\ Use this command to switch between threads.\n\ The new thread ID must be currently known."), - &thread_cmd_list, "thread ", 1, &cmdlist); + &thread_cmd_list, 1, &cmdlist); + + add_com_alias ("t", thread_cmd, class_run, 1); -#define THREAD_APPLY_FLAGS_HELP "\ +#define THREAD_APPLY_OPTION_HELP "\ Prints per-inferior thread number and target system's thread id\n\ followed by COMMAND output.\n\ -FLAG arguments are -q (quiet), -c (continue), -s (silent).\n\ -Flag -q disables printing the thread information.\n\ -By default, if a COMMAND raises an error, thread apply is aborted.\n\ -Flag -c indicates to print the error and continue.\n\ -Flag -s indicates to silently ignore a COMMAND that raises an error\n\ -or produces no output." - - add_prefix_cmd ("apply", class_run, thread_apply_command, - _("Apply a command to a list of threads.\n\ -Usage: thread apply ID... [FLAG]... COMMAND\n\ +\n\ +By default, an error raised during the execution of COMMAND\n\ +aborts \"thread apply\".\n\ +\n\ +Options:\n\ +%OPTIONS%" + + const auto thread_apply_opts = make_thread_apply_options_def_group (nullptr); + + static std::string thread_apply_help = gdb::option::build_help (_("\ +Apply a command to a list of threads.\n\ +Usage: thread apply ID... [OPTION]... COMMAND\n\ ID is a space-separated list of IDs of threads to apply COMMAND on.\n" -THREAD_APPLY_FLAGS_HELP), - &thread_apply_list, "thread apply ", 1, &thread_cmd_list); +THREAD_APPLY_OPTION_HELP), + thread_apply_opts); - add_cmd ("all", class_run, thread_apply_all_command, - _("\ + c = add_prefix_cmd ("apply", class_run, thread_apply_command, + thread_apply_help.c_str (), + &thread_apply_list, 1, + &thread_cmd_list); + set_cmd_completer_handle_brkchars (c, thread_apply_command_completer); + + const auto thread_apply_all_opts + = make_thread_apply_all_options_def_group (nullptr, nullptr); + + static std::string thread_apply_all_help = gdb::option::build_help (_("\ Apply a command to all threads.\n\ \n\ -Usage: thread apply all [-ascending] [FLAG]... COMMAND\n\ --ascending: Call COMMAND for all threads in ascending order.\n\ - The default is descending order.\n" -THREAD_APPLY_FLAGS_HELP), - &thread_apply_list); +Usage: thread apply all [OPTION]... COMMAND\n" +THREAD_APPLY_OPTION_HELP), + thread_apply_all_opts); + + c = add_cmd ("all", class_run, thread_apply_all_command, + thread_apply_all_help.c_str (), + &thread_apply_list); + set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer); - add_com ("taas", class_run, taas_command, _("\ + c = add_com ("taas", class_run, taas_command, _("\ Apply a command to all threads (ignoring errors and empty output).\n\ -Usage: taas COMMAND\n\ -shortcut for 'thread apply all -s COMMAND'")); +Usage: taas [OPTION]... COMMAND\n\ +shortcut for 'thread apply all -s [OPTION]... COMMAND'\n\ +See \"help thread apply all\" for available options.")); + set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer); - add_com ("tfaas", class_run, tfaas_command, _("\ + c = add_com ("tfaas", class_run, tfaas_command, _("\ Apply a command to all frames of all threads (ignoring errors and empty output).\n\ -Usage: tfaas COMMAND\n\ -shortcut for 'thread apply all -s frame apply all -s COMMAND'")); +Usage: tfaas [OPTION]... COMMAND\n\ +shortcut for 'thread apply all -s -- frame apply all -s [OPTION]... COMMAND'\n\ +See \"help frame apply all\" for available options.")); + set_cmd_completer_handle_brkchars (c, frame_apply_all_cmd_completer); add_cmd ("name", class_run, thread_name_command, _("Set the current thread's name.\n\ @@ -2000,8 +2231,6 @@ Usage: thread find REGEXP\n\ Will display thread ids whose name, target ID, or extra info matches REGEXP."), &thread_cmd_list); - add_com_alias ("t", "thread", class_run, 1); - add_setshow_boolean_cmd ("thread-events", no_class, &print_thread_events, _("\ Set printing of thread events (such as thread start and exit)."), _("\ @@ -2010,6 +2239,14 @@ Show printing of thread events (such as thread start and exit)."), NULL, show_print_thread_events, &setprintlist, &showprintlist); + add_setshow_boolean_cmd ("threads", class_maintenance, &debug_threads, _("\ +Set thread debugging."), _("\ +Show thread debugging."), _("\ +When on messages about thread creation and deletion are printed."), + nullptr, + show_debug_threads, + &setdebuglist, &showdebuglist); + create_internalvar_type_lazy ("_thread", &thread_funcs, NULL); create_internalvar_type_lazy ("_gthread", >hread_funcs, NULL); }