[v5,1/2] gdbserver: change 'all_processes' and 'all_threads' list type

Message ID 20241028082018.2564511-2-stephan.rohr@intel.com
State New
Headers
Series gdbserver: add thread map |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gdb_build--master-arm fail Patch failed to apply

Commit Message

Rohr, Stephan Oct. 28, 2024, 8:20 a.m. UTC
  This patch replaces the 'std::list' type of 'all_processes' and
'all_threads' with the more lightweight 'owning_intrusive_list'
type.
---
 gdbserver/gdbthread.h  |  8 ++---
 gdbserver/inferiors.cc | 75 +++++++++++++-----------------------------
 gdbserver/inferiors.h  |  7 ++--
 gdbserver/server.cc    | 16 ++++-----
 4 files changed, 37 insertions(+), 69 deletions(-)
  

Comments

Simon Marchi Oct. 28, 2024, 5:15 p.m. UTC | #1
> @@ -56,7 +55,7 @@ struct thread_info *
>  get_first_thread (void)
>  {
>    if (!all_threads.empty ())
> -    return all_threads.front ();
> +    return &(all_threads.front ());

Unnecessary parenthesis.

> @@ -101,10 +94,9 @@ remove_thread (struct thread_info *thread)
>      target_disable_btrace (thread->btrace);
>  
>    discard_queued_stop_replies (ptid_of (thread));
> -  all_threads.remove (thread);
> +  all_threads.erase (all_threads.iterator_to (*thread));
>    if (current_thread == thread)
>      switch_to_thread (nullptr);
> -  free_one_thread (thread);

switch_to_thread accesses thread, so perhaps move the erase line at the
end?

> @@ -140,11 +131,8 @@ clear_inferiors (void)
>  struct process_info *
>  add_process (int pid, int attached)
>  {
> -  process_info *process = new process_info (pid, attached);
> -
> -  all_processes.push_back (process);
> -
> -  return process;
> +  auto &process = all_processes.emplace_back (pid, attached);
> +  return &process;

  return &all_processes.emplace_back (pid, attached);

> @@ -157,10 +145,9 @@ remove_process (struct process_info *process)
>    clear_symbol_cache (&process->symbol_cache);
>    free_all_breakpoints (process);
>    gdb_assert (find_thread_process (process) == NULL);
> -  all_processes.remove (process);
> +  all_processes.erase (all_processes.iterator_to (*process));
>    if (current_process () == process)
>      switch_to_process (nullptr);
> -  delete process;

switch_to_process does not access `process`, but to be future-proof (if
it ever does), and to mirror the thread removal above, I would move the
erase at the end here as well.

> @@ -177,7 +164,7 @@ process_info *
>  get_first_process (void)
>  {
>    if (!all_processes.empty ())
> -    return all_processes.front ();
> +    return &(all_processes.front ());

Unnecessary parenthesis.

> @@ -236,18 +223,9 @@ for_each_process (gdb::function_view<void (process_info *)> func)
>  process_info *
>  find_process (gdb::function_view<bool (process_info *)> func)
>  {
> -  std::list<process_info *>::iterator next, cur = all_processes.begin ();
> -
> -  while (cur != all_processes.end ())
> -    {
> -      next = cur;
> -      next++;
> -
> -      if (func (*cur))
> -	return *cur;
> -
> -      cur = next;
> -    }
> +  for (process_info &process : all_processes)
> +    if (func (&process))
> +      return &process;

Just to confirm, are we sure that this transformation is safe, nothing
calling `find_process` needed this "safe" iteration?

> @@ -257,18 +235,9 @@ find_process (gdb::function_view<bool (process_info *)> func)
>  thread_info *
>  find_thread (gdb::function_view<bool (thread_info *)> func)
>  {
> -  std::list<thread_info *>::iterator next, cur = all_threads.begin ();
> -
> -  while (cur != all_threads.end ())
> -    {
> -      next = cur;
> -      next++;
> -
> -      if (func (*cur))
> -	return *cur;
> -
> -      cur = next;
> -    }
> +  for (thread_info &thread : all_threads)
> +    if (func (&thread))
> +      return &thread;

Ditto here?

> @@ -2523,7 +2523,7 @@ static void
>  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
>  {
>    client_state &cs = get_client_state ();
> -  static std::list<thread_info *>::const_iterator thread_iter;
> +  static owning_intrusive_list<thread_info>::iterator thread_iter;

I was skeptical of this change, I don't see why we would need to change
const_iterator to iterator.  I then realized that our const_iterator
implementation is broken.

It shouldn't be

  using const_iterator = const intrusive_list_iterator<T, AsNode>;

but

  using const_iterator = intrusive_list_iterator<const T, AsNode>;

But of course fixing this uncovers some mistakes, not trivial to fix.
I'd like to fix it, but in the mean time I think it's ok for you to
change const_iterator -> iterator, as to not delay this patch series
further.

Simon
  
Rohr, Stephan Oct. 29, 2024, 8:54 p.m. UTC | #2
Hi Simon,

thank you for your feedback.

Regarding the "safe" iteration in 'find_thread' and 'find_process': I ran the testsuite for
the 'native-gdbserver' and 'native-extended-gdbserver' targets on Ubuntu22, I could not
find regressions.

Would this be sufficient to assume the change is safe? I haven't tested on other platforms,
but couldn't find relevant calls to 'find_thread' and 'find_process' that modify the
currently iterated thread / process.

If we're sceptical about breaking something, I also don't object going back to the "safe"
iteration.

Thanks
Stephan

> -----Original Message-----
> From: Simon Marchi <simark@simark.ca>
> Sent: Monday, 28 October 2024 18:16
> To: Rohr, Stephan <stephan.rohr@intel.com>; gdb-patches@sourceware.org
> Cc: aburgess@redhat.com; tom@tromey.com
> Subject: Re: [PATCH v5 1/2] gdbserver: change 'all_processes' and 'all_threads'
> list type
> 
> > @@ -56,7 +55,7 @@ struct thread_info *
> >  get_first_thread (void)
> >  {
> >    if (!all_threads.empty ())
> > -    return all_threads.front ();
> > +    return &(all_threads.front ());
> 
> Unnecessary parenthesis.
> 
> > @@ -101,10 +94,9 @@ remove_thread (struct thread_info *thread)
> >      target_disable_btrace (thread->btrace);
> >
> >    discard_queued_stop_replies (ptid_of (thread));
> > -  all_threads.remove (thread);
> > +  all_threads.erase (all_threads.iterator_to (*thread));
> >    if (current_thread == thread)
> >      switch_to_thread (nullptr);
> > -  free_one_thread (thread);
> 
> switch_to_thread accesses thread, so perhaps move the erase line at the
> end?
> 
> > @@ -140,11 +131,8 @@ clear_inferiors (void)
> >  struct process_info *
> >  add_process (int pid, int attached)
> >  {
> > -  process_info *process = new process_info (pid, attached);
> > -
> > -  all_processes.push_back (process);
> > -
> > -  return process;
> > +  auto &process = all_processes.emplace_back (pid, attached);
> > +  return &process;
> 
>   return &all_processes.emplace_back (pid, attached);
> 
> > @@ -157,10 +145,9 @@ remove_process (struct process_info *process)
> >    clear_symbol_cache (&process->symbol_cache);
> >    free_all_breakpoints (process);
> >    gdb_assert (find_thread_process (process) == NULL);
> > -  all_processes.remove (process);
> > +  all_processes.erase (all_processes.iterator_to (*process));
> >    if (current_process () == process)
> >      switch_to_process (nullptr);
> > -  delete process;
> 
> switch_to_process does not access `process`, but to be future-proof (if
> it ever does), and to mirror the thread removal above, I would move the
> erase at the end here as well.
> 
> > @@ -177,7 +164,7 @@ process_info *
> >  get_first_process (void)
> >  {
> >    if (!all_processes.empty ())
> > -    return all_processes.front ();
> > +    return &(all_processes.front ());
> 
> Unnecessary parenthesis.
> 
> > @@ -236,18 +223,9 @@ for_each_process (gdb::function_view<void
> (process_info *)> func)
> >  process_info *
> >  find_process (gdb::function_view<bool (process_info *)> func)
> >  {
> > -  std::list<process_info *>::iterator next, cur = all_processes.begin ();
> > -
> > -  while (cur != all_processes.end ())
> > -    {
> > -      next = cur;
> > -      next++;
> > -
> > -      if (func (*cur))
> > -	return *cur;
> > -
> > -      cur = next;
> > -    }
> > +  for (process_info &process : all_processes)
> > +    if (func (&process))
> > +      return &process;
> 
> Just to confirm, are we sure that this transformation is safe, nothing
> calling `find_process` needed this "safe" iteration?
> 
> > @@ -257,18 +235,9 @@ find_process (gdb::function_view<bool
> (process_info *)> func)
> >  thread_info *
> >  find_thread (gdb::function_view<bool (thread_info *)> func)
> >  {
> > -  std::list<thread_info *>::iterator next, cur = all_threads.begin ();
> > -
> > -  while (cur != all_threads.end ())
> > -    {
> > -      next = cur;
> > -      next++;
> > -
> > -      if (func (*cur))
> > -	return *cur;
> > -
> > -      cur = next;
> > -    }
> > +  for (thread_info &thread : all_threads)
> > +    if (func (&thread))
> > +      return &thread;
> 
> Ditto here?
> 
> > @@ -2523,7 +2523,7 @@ static void
> >  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> >  {
> >    client_state &cs = get_client_state ();
> > -  static std::list<thread_info *>::const_iterator thread_iter;
> > +  static owning_intrusive_list<thread_info>::iterator thread_iter;
> 
> I was skeptical of this change, I don't see why we would need to change
> const_iterator to iterator.  I then realized that our const_iterator
> implementation is broken.
> 
> It shouldn't be
> 
>   using const_iterator = const intrusive_list_iterator<T, AsNode>;
> 
> but
> 
>   using const_iterator = intrusive_list_iterator<const T, AsNode>;
> 
> But of course fixing this uncovers some mistakes, not trivial to fix.
> I'd like to fix it, but in the mean time I think it's ok for you to
> change const_iterator -> iterator, as to not delay this patch series
> further.
> 
> Simon
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Simon Marchi Oct. 30, 2024, 2:12 p.m. UTC | #3
On 2024-10-29 16:54, Rohr, Stephan wrote:
> Hi Simon,
> 
> thank you for your feedback.
> 
> Regarding the "safe" iteration in 'find_thread' and 'find_process': I ran the testsuite for
> the 'native-gdbserver' and 'native-extended-gdbserver' targets on Ubuntu22, I could not
> find regressions.
> 
> Would this be sufficient to assume the change is safe? I haven't tested on other platforms,
> but couldn't find relevant calls to 'find_thread' and 'find_process' that modify the
> currently iterated thread / process.
> 
> If we're sceptical about breaking something, I also don't object going back to the "safe"
> iteration.

I just did a scan of all uses of find_thread and find_process in
gdbserver, and nothing appeared to need the safe iteration, so I think
we are good.  I think I said it earlier, but it would surprise me if
some thread or process were removed during a find_* operation.

Simon
  

Patch

diff --git a/gdbserver/gdbthread.h b/gdbserver/gdbthread.h
index adc5857abfd..1c380d5ff22 100644
--- a/gdbserver/gdbthread.h
+++ b/gdbserver/gdbthread.h
@@ -21,14 +21,12 @@ 
 
 #include "gdbsupport/common-gdbthread.h"
 #include "gdbsupport/function-view.h"
-#include "inferiors.h"
-
-#include <list>
+#include "gdbsupport/owning_intrusive_list.h"
 
 struct btrace_target_info;
 struct regcache;
 
-struct thread_info
+struct thread_info : public intrusive_list_node<thread_info>
 {
   thread_info (ptid_t id, void *target_data)
     : id (id), target_data (target_data)
@@ -86,7 +84,7 @@  struct thread_info
   gdb_thread_options thread_options = 0;
 };
 
-extern std::list<thread_info *> all_threads;
+extern owning_intrusive_list<thread_info> all_threads;
 
 void remove_thread (struct thread_info *thread);
 struct thread_info *add_thread (ptid_t ptid, void *target_data);
diff --git a/gdbserver/inferiors.cc b/gdbserver/inferiors.cc
index 5621db377fb..1d39a8d1a86 100644
--- a/gdbserver/inferiors.cc
+++ b/gdbserver/inferiors.cc
@@ -19,11 +19,12 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "gdbsupport/common-inferior.h"
+#include "gdbsupport/owning_intrusive_list.h"
 #include "gdbthread.h"
 #include "dll.h"
 
-std::list<process_info *> all_processes;
-std::list<thread_info *> all_threads;
+owning_intrusive_list<process_info> all_processes;
+owning_intrusive_list<thread_info> all_threads;
 
 /* The current process.  */
 static process_info *current_process_;
@@ -40,14 +41,12 @@  static std::string current_inferior_cwd;
 struct thread_info *
 add_thread (ptid_t thread_id, void *target_data)
 {
-  thread_info *new_thread = new thread_info (thread_id, target_data);
-
-  all_threads.push_back (new_thread);
+  auto &new_thread = all_threads.emplace_back (thread_id, target_data);
 
   if (current_thread == NULL)
-    switch_to_thread (new_thread);
+    switch_to_thread (&new_thread);
 
-  return new_thread;
+  return &new_thread;
 }
 
 /* See gdbthread.h.  */
@@ -56,7 +55,7 @@  struct thread_info *
 get_first_thread (void)
 {
   if (!all_threads.empty ())
-    return all_threads.front ();
+    return &(all_threads.front ());
   else
     return NULL;
 }
@@ -88,12 +87,6 @@  find_any_thread_of_pid (int pid)
   });
 }
 
-static void
-free_one_thread (thread_info *thread)
-{
-  delete thread;
-}
-
 void
 remove_thread (struct thread_info *thread)
 {
@@ -101,10 +94,9 @@  remove_thread (struct thread_info *thread)
     target_disable_btrace (thread->btrace);
 
   discard_queued_stop_replies (ptid_of (thread));
-  all_threads.remove (thread);
+  all_threads.erase (all_threads.iterator_to (*thread));
   if (current_thread == thread)
     switch_to_thread (nullptr);
-  free_one_thread (thread);
 }
 
 void *
@@ -128,7 +120,6 @@  set_thread_regcache_data (struct thread_info *thread, struct regcache *data)
 void
 clear_inferiors (void)
 {
-  for_each_thread (free_one_thread);
   all_threads.clear ();
 
   clear_dlls ();
@@ -140,11 +131,8 @@  clear_inferiors (void)
 struct process_info *
 add_process (int pid, int attached)
 {
-  process_info *process = new process_info (pid, attached);
-
-  all_processes.push_back (process);
-
-  return process;
+  auto &process = all_processes.emplace_back (pid, attached);
+  return &process;
 }
 
 /* Remove a process from the common process list and free the memory
@@ -157,10 +145,9 @@  remove_process (struct process_info *process)
   clear_symbol_cache (&process->symbol_cache);
   free_all_breakpoints (process);
   gdb_assert (find_thread_process (process) == NULL);
-  all_processes.remove (process);
+  all_processes.erase (all_processes.iterator_to (*process));
   if (current_process () == process)
     switch_to_process (nullptr);
-  delete process;
 }
 
 process_info *
@@ -177,7 +164,7 @@  process_info *
 get_first_process (void)
 {
   if (!all_processes.empty ())
-    return all_processes.front ();
+    return &(all_processes.front ());
   else
     return NULL;
 }
@@ -220,13 +207,13 @@  current_process (void)
 void
 for_each_process (gdb::function_view<void (process_info *)> func)
 {
-  std::list<process_info *>::iterator next, cur = all_processes.begin ();
+  owning_intrusive_list<process_info>::iterator next, cur = all_processes.begin ();
 
   while (cur != all_processes.end ())
     {
       next = cur;
       next++;
-      func (*cur);
+      func (&*cur);
       cur = next;
     }
 }
@@ -236,18 +223,9 @@  for_each_process (gdb::function_view<void (process_info *)> func)
 process_info *
 find_process (gdb::function_view<bool (process_info *)> func)
 {
-  std::list<process_info *>::iterator next, cur = all_processes.begin ();
-
-  while (cur != all_processes.end ())
-    {
-      next = cur;
-      next++;
-
-      if (func (*cur))
-	return *cur;
-
-      cur = next;
-    }
+  for (process_info &process : all_processes)
+    if (func (&process))
+      return &process;
 
   return NULL;
 }
@@ -257,18 +235,9 @@  find_process (gdb::function_view<bool (process_info *)> func)
 thread_info *
 find_thread (gdb::function_view<bool (thread_info *)> func)
 {
-  std::list<thread_info *>::iterator next, cur = all_threads.begin ();
-
-  while (cur != all_threads.end ())
-    {
-      next = cur;
-      next++;
-
-      if (func (*cur))
-	return *cur;
-
-      cur = next;
-    }
+  for (thread_info &thread : all_threads)
+    if (func (&thread))
+      return &thread;
 
   return NULL;
 }
@@ -299,13 +268,13 @@  find_thread (ptid_t filter, gdb::function_view<bool (thread_info *)> func)
 void
 for_each_thread (gdb::function_view<void (thread_info *)> func)
 {
-  std::list<thread_info *>::iterator next, cur = all_threads.begin ();
+  owning_intrusive_list<thread_info>::iterator next, cur = all_threads.begin ();
 
   while (cur != all_threads.end ())
     {
       next = cur;
       next++;
-      func (*cur);
+      func (&*cur);
       cur = next;
     }
 }
diff --git a/gdbserver/inferiors.h b/gdbserver/inferiors.h
index 7d453260270..3f2c5c70a39 100644
--- a/gdbserver/inferiors.h
+++ b/gdbserver/inferiors.h
@@ -20,8 +20,9 @@ 
 #define GDBSERVER_INFERIORS_H
 
 #include "gdbsupport/gdb_vecs.h"
+#include "gdbsupport/owning_intrusive_list.h"
+
 #include "dll.h"
-#include <list>
 
 struct thread_info;
 struct regcache;
@@ -32,7 +33,7 @@  struct raw_breakpoint;
 struct fast_tracepoint_jump;
 struct process_info_private;
 
-struct process_info
+struct process_info : public intrusive_list_node<process_info>
 {
   process_info (int pid_, int attached_)
   : pid (pid_), attached (attached_)
@@ -99,7 +100,7 @@  pid_of (const process_info *proc)
 struct process_info *current_process (void);
 struct process_info *get_thread_process (const struct thread_info *);
 
-extern std::list<process_info *> all_processes;
+extern owning_intrusive_list<process_info> all_processes;
 
 /* Invoke FUNC for each process.  */
 
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 5190df4aed5..5058428f58b 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1355,15 +1355,15 @@  handle_detach (char *own_buf)
      another process might delete the next thread in the iteration, which is
      the one saved by the safe iterator.  We will never delete the currently
      iterated on thread, so standard iteration should be safe.  */
-  for (thread_info *thread : all_threads)
+  for (thread_info &thread : all_threads)
     {
       /* Only threads that are of the process we are detaching.  */
-      if (thread->id.pid () != pid)
+      if (thread.id.pid () != pid)
 	continue;
 
       /* Only threads that have a pending fork event.  */
       target_waitkind kind;
-      thread_info *child = target_thread_pending_child (thread, &kind);
+      thread_info *child = target_thread_pending_child (&thread, &kind);
       if (child == nullptr || kind == TARGET_WAITKIND_THREAD_CLONED)
 	continue;
 
@@ -1375,7 +1375,7 @@  handle_detach (char *own_buf)
       if (detach_inferior (fork_child_process) != 0)
 	warning (_("Failed to detach fork child %s, child of %s"),
 		 target_pid_to_str (ptid_t (fork_child_pid)).c_str (),
-		 target_pid_to_str (thread->id).c_str ());
+		 target_pid_to_str (thread.id).c_str ());
     }
 
   if (detach_inferior (process) != 0)
@@ -2523,7 +2523,7 @@  static void
 handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 {
   client_state &cs = get_client_state ();
-  static std::list<thread_info *>::const_iterator thread_iter;
+  static owning_intrusive_list<thread_info>::iterator thread_iter;
 
   /* Reply the current thread id.  */
   if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
@@ -2536,7 +2536,7 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       else
 	{
 	  thread_iter = all_threads.begin ();
-	  ptid = (*thread_iter)->id;
+	  ptid = thread_iter->id;
 	}
 
       sprintf (own_buf, "QC");
@@ -2599,7 +2599,7 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	  thread_iter = all_threads.begin ();
 
 	  *own_buf++ = 'm';
-	  ptid_t ptid = (*thread_iter)->id;
+	  ptid_t ptid = thread_iter->id;
 	  write_ptid (own_buf, ptid);
 	  thread_iter++;
 	  return;
@@ -2611,7 +2611,7 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	  if (thread_iter != all_threads.end ())
 	    {
 	      *own_buf++ = 'm';
-	      ptid_t ptid = (*thread_iter)->id;
+	      ptid_t ptid = thread_iter->id;
 	      write_ptid (own_buf, ptid);
 	      thread_iter++;
 	      return;