From cd9629e1df1a280c19e1daaf6c1195afbab0aca9 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Thu, 19 Dec 2019 14:19:41 -0500 Subject: [PATCH] Change inferior thread list to be a thread map Remove the inferior::thread_list linked list, replace with inferior::thread_map, an std::map indexed by ptid. This should make it faster to look up a thread from its ptid. --- gdb/gdbarch-selftests.c | 4 +- gdb/gdbthread.h | 10 +- gdb/inferior.c | 14 +-- gdb/inferior.h | 22 ++-- gdb/mi/mi-main.c | 28 +++-- gdb/regcache.c | 2 +- gdb/thread-iter.c | 55 +++++++--- gdb/thread-iter.h | 50 +++++++-- gdb/thread-map.h | 133 ++++++++++++++++++++++++ gdb/thread.c | 224 ++++++++++++++++++++++------------------ 10 files changed, 381 insertions(+), 161 deletions(-) create mode 100644 gdb/thread-map.h diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c index 47f4d99d18..6cfaa2cdd1 100644 --- a/gdb/gdbarch-selftests.c +++ b/gdb/gdbarch-selftests.c @@ -81,9 +81,7 @@ register_to_value_test (struct gdbarch *gdbarch) mock_inferior.gdbarch = gdbarch; mock_inferior.aspace = &mock_aspace; thread_info mock_thread (&mock_inferior, mock_ptid); - - scoped_restore restore_thread_list - = make_scoped_restore (&mock_inferior.thread_list, &mock_thread); + mock_inferior.thread_map[mock_ptid] = &mock_thread; /* Add the mock inferior to the inferior list so that look ups by target+ptid can find it. */ diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index f205e29dd7..45d2f2f8ee 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -252,7 +252,6 @@ public: /* Mark this thread as running and notify observers. */ void set_running (bool running); - struct thread_info *next = NULL; ptid_t ptid; /* "Actual process id"; In fact, this may be overloaded with kernel thread id, etc. */ @@ -433,14 +432,21 @@ extern struct thread_info *add_thread_with_info (process_stratum_target *targ, ptid_t ptid, private_thread_info *); -/* Delete an existing thread list entry. */ +/* Delete an existing thread, removing the entry from its inferior's thread + map. */ extern void delete_thread (struct thread_info *thread); +/* Like the above, but don't remove the entry from the inferior thread map. */ +extern void delete_thread_noremove(struct thread_info *thread); + /* Delete an existing thread list entry, and be quiet about it. Used after the process this thread having belonged to having already exited, for example. */ extern void delete_thread_silent (struct thread_info *thread); +/* Like the above, but don't remove the entry from the inferior thread map. */ +extern void delete_thread_silent_noremove (thread_info *thread); + /* Delete a step_resume_breakpoint from the thread database. */ extern void delete_step_resume_breakpoint (struct thread_info *); diff --git a/gdb/inferior.c b/gdb/inferior.c index c2e9da3d3d..47dfca04bb 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -150,8 +150,8 @@ delete_inferior (struct inferior *todel) if (!inf) return; - for (thread_info *tp : inf->threads_safe ()) - delete_thread_silent (tp); + for (thread_info *tp : inf->threads ()) + delete_thread_silent_noremove (tp); if (infprev) infprev->next = inf->next; @@ -182,14 +182,16 @@ exit_inferior_1 (struct inferior *inftoex, int silent) if (!inf) return; - for (thread_info *tp : inf->threads_safe ()) + for (thread_info *tp : inf->threads ()) { if (silent) - delete_thread_silent (tp); + delete_thread_silent_noremove (tp); else - delete_thread (tp); + delete_thread_noremove (tp); } + inf->thread_map.clear (); + gdb::observers::inferior_exit.notify (inf); inf->pid = 0; @@ -640,7 +642,7 @@ inferior_command (const char *args, int from_tty) { if (inf != current_inferior ()) { - thread_info *tp = any_thread_of_inferior (inf); + thread_info *tp = first_thread_of_inferior (inf); if (tp == NULL) error (_("Inferior has no threads.")); diff --git a/gdb/inferior.h b/gdb/inferior.h index 4229c6017d..4883282f63 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -55,6 +55,7 @@ struct thread_info; #include "gdbsupport/common-inferior.h" #include "gdbthread.h" +#include "thread-map.h" #include "process-stratum-target.h" @@ -381,7 +382,7 @@ public: struct inferior *next = NULL; /* This inferior's thread list. */ - thread_info *thread_list = nullptr; + ptid_thread_map thread_map; /* Returns a range adapter covering the inferior's threads, including exited threads. Used like this: @@ -389,8 +390,8 @@ public: for (thread_info *thr : inf->threads ()) { .... } */ - inf_threads_range threads () - { return inf_threads_range (this->thread_list); } + all_thread_map_range threads () + { return all_thread_map_range (this->thread_map); } /* Returns a range adapter covering the inferior's non-exited threads. Used like this: @@ -398,19 +399,8 @@ public: for (thread_info *thr : inf->non_exited_threads ()) { .... } */ - inf_non_exited_threads_range non_exited_threads () - { return inf_non_exited_threads_range (this->thread_list); } - - /* Like inferior::threads(), but returns a range adapter that can be - used with range-for, safely. I.e., it is safe to delete the - currently-iterated thread, like this: - - for (thread_info *t : inf->threads_safe ()) - if (some_condition ()) - delete f; - */ - inline safe_inf_threads_range threads_safe () - { return safe_inf_threads_range (this->thread_list); } + non_exited_thread_map_range non_exited_threads () + { return non_exited_thread_map_range (this->thread_map); } /* Convenient handle (GDB inferior id). Unique across all inferiors. */ diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 24daf3f883..19c4c54b9d 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -252,15 +252,6 @@ proceed_thread (struct thread_info *thread, int pid) proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT); } -static int -proceed_thread_callback (struct thread_info *thread, void *arg) -{ - int pid = *(int *)arg; - - proceed_thread (thread, pid); - return 0; -} - static void exec_continue (char **argv, int argc) { @@ -286,7 +277,24 @@ exec_continue (char **argv, int argc) pid = inf->pid; } - iterate_over_threads (proceed_thread_callback, &pid); + + /* Proceed the threads in global number order. This is not necessary, + it's just to avoid breaking some tests like gdb.mi/mi-nsintrall.exp + that expect the *running notifications in that order. In the end, + we should instead fix the test to accept the notifications in any + order. */ + std::vector threads; + for (thread_info *tp : all_threads ()) + threads.push_back (tp); + + std::sort (threads.begin (), threads.end (), + [] (thread_info *a, thread_info *b) + { + return a->global_num < b->global_num; + }); + + for (thread_info *tp : threads) + proceed_thread (tp, pid); } else { diff --git a/gdb/regcache.c b/gdb/regcache.c index b9f1b0a349..2dfe8eaf5f 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -1593,7 +1593,7 @@ cooked_read_test (struct gdbarch *gdbarch) mock_inferior.gdbarch = gdbarch; mock_inferior.aspace = &mock_aspace; thread_info mock_thread (&mock_inferior, mock_ptid); - mock_inferior.thread_list = &mock_thread; + mock_inferior.thread_map[mock_ptid] = &mock_thread; /* Add the mock inferior to the inferior list so that look ups by target+ptid can find it. */ diff --git a/gdb/thread-iter.c b/gdb/thread-iter.c index a6bbc50185..b7cf4d0e1c 100644 --- a/gdb/thread-iter.c +++ b/gdb/thread-iter.c @@ -27,8 +27,13 @@ all_threads_iterator::all_threads_iterator (begin_t) { /* Advance M_INF/M_THR to the first thread's position. */ for (m_inf = inferior_list; m_inf != NULL; m_inf = m_inf->next) - if ((m_thr = m_inf->thread_list) != NULL) - return; + { + if (!m_inf->thread_map.empty()) + { + this->m_thr_iter = m_inf->thread_map.cbegin (); + return; + } + } } /* See thread-iter.h. */ @@ -43,16 +48,24 @@ all_threads_iterator::advance () for (; m_inf != NULL; m_inf = m_inf->next) { - m_thr = m_inf->thread_list; - while (m_thr != NULL) + m_thr_iter = m_inf->thread_map.cbegin (); + while (m_thr_iter != m_inf->thread_map.cend ()) { return; start: - m_thr = m_thr->next; + ++m_thr_iter; } } } +thread_info *all_threads_iterator::operator* () const +{ + gdb_assert (m_inf != NULL); + gdb_assert (m_thr_iter != m_inf->thread_map.cend ()); + + return m_thr_iter->second; +} + /* See thread-iter.h. */ bool @@ -74,12 +87,19 @@ all_matching_threads_iterator::all_matching_threads_iterator gdb_assert ((filter_target == nullptr && filter_ptid == minus_one_ptid) || filter_target->stratum () == process_stratum); - m_thr = nullptr; for (m_inf = inferior_list; m_inf != NULL; m_inf = m_inf->next) - if (m_inf_matches ()) - for (m_thr = m_inf->thread_list; m_thr != NULL; m_thr = m_thr->next) - if (m_thr->ptid.matches (m_filter_ptid)) - return; + { + if (!m_inf->thread_map.empty () && m_inf_matches ()) + { + for (m_thr_iter = m_inf->thread_map.cbegin (); + m_thr_iter != m_inf->thread_map.cend (); + m_thr_iter++) + { + if (m_thr_iter->second->ptid.matches (m_filter_ptid)) + return; + } + } + } } /* See thread-iter.h. */ @@ -95,13 +115,20 @@ all_matching_threads_iterator::advance () for (; m_inf != NULL; m_inf = m_inf->next) if (m_inf_matches ()) { - m_thr = m_inf->thread_list; - while (m_thr != NULL) + m_thr_iter = m_inf->thread_map.cbegin (); + while (m_thr_iter != m_inf->thread_map.cend ()) { - if (m_thr->ptid.matches (m_filter_ptid)) + if (m_thr_iter->second->ptid.matches (m_filter_ptid)) return; start: - m_thr = m_thr->next; + ++m_thr_iter; } } } + +thread_info *all_matching_threads_iterator::operator* () const +{ + gdb_assert (m_inf != nullptr); + + return m_thr_iter->second; +} diff --git a/gdb/thread-iter.h b/gdb/thread-iter.h index 95dc562436..e2e5783f16 100644 --- a/gdb/thread-iter.h +++ b/gdb/thread-iter.h @@ -22,6 +22,7 @@ #include "gdbsupport/filtered-iterator.h" #include "gdbsupport/next-iterator.h" #include "gdbsupport/safe-iterator.h" +#include "thread-map.h" /* A forward iterator that iterates over a given inferior's threads. */ @@ -50,10 +51,10 @@ public: /* Create a one-past-end iterator. */ all_threads_iterator () - : m_thr (nullptr) + : m_inf (nullptr) {} - thread_info *operator* () const { return m_thr; } + thread_info *operator* () const; all_threads_iterator &operator++ () { @@ -62,20 +63,34 @@ public: } bool operator== (const all_threads_iterator &other) const - { return m_thr == other.m_thr; } + { + if (m_inf != other.m_inf) + return false; + + /* Inferiors of both iterators are equal. */ + if (m_inf == NULL) { + /* They are both ended iterators. */ + return true; + } else { + /* Do they both point to the same thread? */ + return m_thr_iter == other.m_thr_iter; + } + } bool operator!= (const all_threads_iterator &other) const - { return m_thr != other.m_thr; } + { + return !(*this == other); + } private: /* Advance to the next thread. */ void advance (); private: - /* The current inferior and thread. M_THR is NULL if we reached the + /* The current inferior and thread. M_INF is NULL if we reached the end of the threads list of the last inferior. */ inferior *m_inf; - thread_info *m_thr; + ptid_thread_map::const_iterator m_thr_iter; }; /* Iterate over all threads that match a given PTID. */ @@ -98,12 +113,11 @@ public: /* Create a one-past-end iterator. */ all_matching_threads_iterator () : m_inf (nullptr), - m_thr (nullptr), m_filter_target (nullptr), m_filter_ptid (minus_one_ptid) {} - thread_info *operator* () const { return m_thr; } + thread_info *operator* () const; all_matching_threads_iterator &operator++ () { @@ -112,10 +126,24 @@ public: } bool operator== (const all_matching_threads_iterator &other) const - { return m_thr == other.m_thr; } + { + if (m_inf != other.m_inf) + return false; + + /* Inferiors of both iterators are equal. */ + if (m_inf == NULL) { + /* They are both ended iterators. */ + return true; + } else { + /* Do they both point to the same thread? */ + return m_thr_iter == other.m_thr_iter; + } + } bool operator!= (const all_matching_threads_iterator &other) const - { return m_thr != other.m_thr; } + { + return !(* this == other); + } private: /* Advance to next thread, skipping filtered threads. */ @@ -130,7 +158,7 @@ private: inferior *m_inf; /* The current thread. */ - thread_info *m_thr; + ptid_thread_map::const_iterator m_thr_iter; /* The filter. */ process_stratum_target *m_filter_target; diff --git a/gdb/thread-map.h b/gdb/thread-map.h new file mode 100644 index 0000000000..2b41b7361c --- /dev/null +++ b/gdb/thread-map.h @@ -0,0 +1,133 @@ +/* Thread map type for GDB, the GNU debugger. + Copyright (C) 2019 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef THREAD_MAP_H +#define THREAD_MAP_H + +#include "defs.h" + +#include + +struct thread_info; + +struct hash_ptid +{ + size_t operator() (const ptid_t &ptid) const + { + std::hash long_hash; + return long_hash(ptid.pid()) + long_hash(ptid.lwp()) + long_hash(ptid.tid()); + } +}; + +using ptid_thread_map = std::unordered_map; + +struct all_thread_map_range_iterator +{ + all_thread_map_range_iterator (ptid_thread_map::const_iterator iter) + : m_iter (iter) + {} + + bool operator!= (const all_thread_map_range_iterator &other) + { return this->m_iter != other.m_iter; } + + void operator++ () + { this->m_iter++; } + + thread_info *operator* () + { return this->m_iter->second; } + +private: + typename ptid_thread_map::const_iterator m_iter; +}; + +struct all_thread_map_range +{ + all_thread_map_range (const ptid_thread_map &map) + : m_map (map) + {} + + all_thread_map_range_iterator begin () + { + return all_thread_map_range_iterator (this->m_map.begin ()); + } + + all_thread_map_range_iterator end () + { + return all_thread_map_range_iterator (this->m_map.end ()); + } + +private: + const ptid_thread_map &m_map; +}; + +struct non_exited_thread_map_range_iterator +{ + non_exited_thread_map_range_iterator (typename ptid_thread_map::const_iterator iter, + typename ptid_thread_map::const_iterator end) + : m_iter (iter), m_end (end) + { + advante_to_next_matching (); + } + + bool operator!= (const non_exited_thread_map_range_iterator &other) + { return this->m_iter != other.m_iter; } + + void operator++ () + { + this->m_iter++; + advante_to_next_matching (); + } + + thread_info *operator* () + { return this->m_iter->second; } + +private: + typename ptid_thread_map::const_iterator m_iter; + typename ptid_thread_map::const_iterator m_end; + + void advante_to_next_matching () + { + while (this->m_iter != this->m_end + && this->m_iter->second->state == THREAD_EXITED) + { + this->m_iter++; + } + } +}; + +struct non_exited_thread_map_range +{ + non_exited_thread_map_range (const ptid_thread_map &map) + : m_map (map) + {} + + non_exited_thread_map_range_iterator begin() + { + return non_exited_thread_map_range_iterator (this->m_map.begin (), this->m_map.end ()); + } + + non_exited_thread_map_range_iterator end() + { + return non_exited_thread_map_range_iterator (this->m_map.end (), this->m_map.end ()); + } + +private: + const ptid_thread_map &m_map; +}; + +#endif diff --git a/gdb/thread.c b/gdb/thread.c index 03fdc05140..20b83ea3af 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -226,7 +226,7 @@ init_thread_list (void) else set_thread_exited (tp, 1); - inf->thread_list = NULL; + inf->thread_map.clear(); } } @@ -238,16 +238,10 @@ 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; + /* A thread with this ptid should not exist yet. */ + gdb_assert (inf->thread_map.find (ptid) == inf->thread_map.end ()); - for (last = inf->thread_list; last->next != NULL; last = last->next) - ; - last->next = tp; - } + inf->thread_map[ptid] = tp; return tp; } @@ -453,33 +447,25 @@ thread_step_over_chain_remove (struct thread_info *tp) THR must not be NULL or a failed assertion will be raised. */ static void -delete_thread_1 (thread_info *thr, bool silent) +delete_thread_1 (thread_info *thr, bool silent, bool remove) { gdb_assert (thr != nullptr); - struct thread_info *tp, *tpprev = NULL; - - 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; + if (remove) + { + size_t nr_deleted = thr->inf->thread_map.erase(thr->ptid); + gdb_assert (nr_deleted == 1); + } - delete tp; + delete thr; } /* Delete thread THREAD and notify of thread exit. If this is the @@ -490,13 +476,25 @@ delete_thread_1 (thread_info *thr, bool silent) void delete_thread (thread_info *thread) { - delete_thread_1 (thread, false /* not silent */); + delete_thread_1 (thread, false /* not silent */, true /* remove */); +} + +void +delete_thread_noremove (thread_info *thread) +{ + delete_thread_1 (thread, false /* silent */, false /* don't remove */); } void delete_thread_silent (thread_info *thread) { - delete_thread_1 (thread, true /* silent */); + delete_thread_1 (thread, true /* silent */, true /* remove */); +} + +void +delete_thread_silent_noremove (thread_info *thread) +{ + delete_thread_1 (thread, true /* silent */, false /* don't remove */); } struct thread_info * @@ -625,7 +623,17 @@ in_thread_list (process_stratum_target *targ, ptid_t ptid) thread_info * first_thread_of_inferior (inferior *inf) { - return inf->thread_list; + gdb_assert (!inf->thread_map.empty ()); + + auto compare_by_per_inf_num = [] (const ptid_thread_map::value_type &a, + const ptid_thread_map::value_type &b) + { + return a.second->per_inf_num < b.second->per_inf_num; + }; + auto it = std::min_element (inf->thread_map.begin (), inf->thread_map.end (), + compare_by_per_inf_num); + + return it->second; } thread_info * @@ -806,7 +814,13 @@ thread_change_ptid (process_stratum_target *targ, inf->pid = new_ptid.pid (); tp = find_thread_ptid (inf, old_ptid); + gdb_assert (tp != nullptr); + + int num_erased = inf->thread_map.erase (old_ptid); + gdb_assert (num_erased == 1); + tp->ptid = new_ptid; + inf->thread_map[new_ptid] = tp; gdb::observers::thread_ptid_changed.notify (old_ptid, new_ptid); } @@ -1127,88 +1141,102 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, } for (inferior *inf : all_inferiors ()) - for (thread_info *tp : inf->threads ()) - { - int core; + { + /* Print the threads in per-inferior number order. */ + std::vector threads_to_print; - any_thread = true; - if (tp == current_thread && tp->state == THREAD_EXITED) - current_exited = true; + for (thread_info *tp : inf->threads ()) + threads_to_print.push_back (tp); - if (!should_print_thread (requested_threads, default_inf_num, - global_ids, pid, tp)) - continue; + std::sort (threads_to_print.begin (), threads_to_print.end (), + [] (thread_info *a, thread_info *b) + { + return a->per_inf_num < b->per_inf_num; + }); - ui_out_emit_tuple tuple_emitter (uiout, NULL); + for (thread_info *tp : threads_to_print) + { + int core; - if (!uiout->is_mi_like_p ()) - { - if (tp == current_thread) - uiout->field_string ("current", "*"); - else - uiout->field_skip ("current"); + any_thread = true; + if (tp == current_thread && tp->state == THREAD_EXITED) + current_exited = true; - uiout->field_string ("id-in-tg", print_thread_id (tp)); - } + if (!should_print_thread (requested_threads, default_inf_num, + global_ids, pid, tp)) + continue; - if (show_global_ids || uiout->is_mi_like_p ()) - uiout->field_signed ("id", tp->global_num); + ui_out_emit_tuple tuple_emitter (uiout, NULL); - /* Switch to the thread (and inferior / target). */ - switch_to_thread (tp); + if (!uiout->is_mi_like_p ()) + { + if (tp == current_thread) + uiout->field_string ("current", "*"); + else + uiout->field_skip ("current"); - /* 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. */ + uiout->field_string ("id-in-tg", print_thread_id (tp)); + } - if (uiout->is_mi_like_p ()) - { - uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); + if (show_global_ids || uiout->is_mi_like_p ()) + uiout->field_signed ("id", tp->global_num); - const char *extra_info = target_extra_thread_info (tp); - if (extra_info != nullptr) - uiout->field_string ("details", extra_info); + /* Switch to the thread (and inferior / target). */ + switch_to_thread (tp); - 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 ()); - } + /* 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. */ - 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 (uiout->is_mi_like_p ()) + { + uiout->field_string ("target-id", target_pid_to_str (tp->ptid)); - if (uiout->is_mi_like_p ()) - { - const char *state = "stopped"; + const char *extra_info = target_extra_thread_info (tp); + if (extra_info != nullptr) + uiout->field_string ("details", extra_info); - if (tp->state == THREAD_RUNNING) - state = "running"; - uiout->field_string ("state", state); - } + 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 ()); + } - core = target_core_of_thread (tp->ptid); - if (uiout->is_mi_like_p () && core != -1) - uiout->field_signed ("core", core); - } + 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 (uiout->is_mi_like_p ()) + { + const char *state = "stopped"; + + 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 @@ -2099,7 +2127,7 @@ update_threads_executing (void) /* If the process has no threads, then it must be we have a process-exit event pending. */ - if (inf->thread_list == NULL) + if (inf->thread_map.empty ()) { targ->threads_executing = true; return; -- 2.34.1