From 1783be1c9161479a158a4e52dd0663f45e6b92e9 Mon Sep 17 00:00:00 2001
From: Aditya Vidyadhar Kamath <Aditya.Kamath1@ibm.com>
Date: Fri, 27 Jan 2023 08:19:56 -0600
Subject: [PATCH] Fix multi-thread debug bug in AIX

---
 gdb/aix-thread.c     | 189 ++++++++++++++++++++++++++-----------------
 gdb/rs6000-aix-nat.c |  10 +++
 gdb/solib-aix.c      |  15 ++++
 3 files changed, 141 insertions(+), 73 deletions(-)

diff --git a/gdb/aix-thread.c b/gdb/aix-thread.c
index e556c153576..c6231e92f91 100644
--- a/gdb/aix-thread.c
+++ b/gdb/aix-thread.c
@@ -55,6 +55,7 @@
 #include <sys/reg.h>
 #include <sched.h>
 #include <sys/pthdebug.h>
+#include <map>
 
 #if !HAVE_DECL_GETTHRDS
 extern int getthrds (pid_t, struct thrdsinfo64 *, int, tid_t *, int);
@@ -70,7 +71,7 @@ static bool debug_aix_thread;
 
 /* Return whether to treat PID as a debuggable thread id.  */
 
-#define PD_TID(ptid)	(pd_active && ptid.tid () != 0)
+#define PD_TID(ptid)	(tmap[ptid.pid ()].pd_active && ptid.tid () != 0)
 
 /* Success and failure values returned by pthdb callbacks.  */
 
@@ -149,14 +150,6 @@ static aix_thread_target aix_thread_ops;
 
 static CORE_ADDR pd_brk_addr;
 
-/* Whether the current application is debuggable by pthdb.  */
-
-static int pd_able = 0;
-
-/* Whether a threaded application is being debugged.  */
-
-static int pd_active = 0;
-
 /* Whether the current architecture is 64-bit.  
    Only valid when pd_able is true.  */
 
@@ -191,9 +184,21 @@ static pthdb_callbacks_t pd_callbacks = {
   NULL
 };
 
-/* Current pthdb session.  */
+/* Aix variable structure.  */
+struct aix_thread_variables 
+{
+  /* Whether the current application is debuggable by pthdb.  */
+  int pd_able;
 
-static pthdb_session_t pd_session;
+  /* Whether a threaded application is being debugged.  */
+  int pd_active;
+
+  /* Current pthdb session.  */
+  pthdb_session_t pd_session;
+};
+
+/* Collection of Aix variables per inferior.  */
+static std::map<pid_t, aix_thread_variables> tmap;
 
 /* Return a printable representation of pthdebug function return
    STATUS.  */
@@ -508,14 +513,13 @@ pdc_read_data (pthdb_user_t user_current_pid, void *buf,
   /* This is needed to eliminate the dependency of current thread
      which is null so that thread reads the correct target memory.  */
   {
-    scoped_restore_current_thread restore_current_thread;
+    scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
     /* Before the first inferior is added, we pass inferior_ptid.pid ()
        from pd_enable () which is 0.  There is no need to switch threads
        during first initialisation.  In the rest of the callbacks the
        current thread needs to be correct.  */
     if (user_current_pid != 0)
-      switch_to_thread (current_inferior ()->process_target (),
-			ptid_t (user_current_pid));
+      inferior_ptid = ptid_t (user_current_pid);
     status = target_read_memory (addr, (gdb_byte *) buf, len);
   }
   ret = status == 0 ? PDC_SUCCESS : PDC_FAILURE;
@@ -639,36 +643,32 @@ pcmp (const void *p1v, const void *p2v)
   return p1->pthid < p2->pthid ? -1 : p1->pthid > p2->pthid;
 }
 
-/* iterate_over_threads() callback for counting GDB threads.
-
-   Do not count the main thread (whose tid is zero).  This matches
-   the list of threads provided by the pthreaddebug library, which
-   does not include that main thread either, and thus allows us
-   to compare the two lists.  */
+/* Callback for counting GDB threads for process pid.  */
 
 static int
-giter_count (struct thread_info *thread, void *countp)
+giter_count (pid_t pid)
 {
-  if (PD_TID (thread->ptid))
-    (*(int *) countp)++;
-  return 0;
+  int gcount = 0;
+  process_stratum_target *proc_target
+    = current_inferior ()->process_target ();
+  for (thread_info *tp : all_threads (proc_target, ptid_t (pid)))
+    gcount++;
+  return gcount;
 }
 
-/* iterate_over_threads() callback for accumulating GDB thread pids.
-
-   Do not include the main thread (whose tid is zero).  This matches
-   the list of threads provided by the pthreaddebug library, which
-   does not include that main thread either, and thus allows us
-   to compare the two lists.  */
+/* Callback for accumulating GDB thread pids.  */
 
 static int
-giter_accum (struct thread_info *thread, void *bufp)
+giter_accum (void *bufp, pid_t pid)
 {
-  if (PD_TID (thread->ptid))
-    {
-      **(struct thread_info ***) bufp = thread;
-      (*(struct thread_info ***) bufp)++;
-    }
+  process_stratum_target *proc_target
+    = current_inferior ()->process_target ();
+  for (thread_info *tp : all_threads (proc_target, ptid_t (pid)))
+  {
+    **(struct thread_info ***) bufp = tp;
+    (*(struct thread_info ***) bufp)++;
+  }
+
   return 0;
 }
 
@@ -719,7 +719,10 @@ get_signaled_thread (int pid)
 		    sizeof (thrinf), &ktid, 1) != 1)
 	break;
 
-      if (thrinf.ti_cursig == SIGTRAP)
+      /* We also need to keep in mind Trap and interrupt or any
+         signal that needs to be handled in pd_update ().  */
+
+      if (thrinf.ti_cursig)
 	return thrinf.ti_tid;
     }
 
@@ -750,6 +753,9 @@ sync_threadlists (int pid)
   pthdb_pthread_t pdtid;
   pthread_t pthid;
   pthdb_tid_t tid;
+  process_stratum_target *proc_target
+            = current_inferior ()->process_target ();
+  thread_info  *tp;
 
   /* Accumulate an array of libpthdebug threads sorted by pthread id.  */
 
@@ -759,11 +765,11 @@ sync_threadlists (int pid)
 
   for (cmd = PTHDB_LIST_FIRST;; cmd = PTHDB_LIST_NEXT)
     {
-      status = pthdb_pthread (pd_session, &pdtid, cmd);
+      status = pthdb_pthread (tmap[pid].pd_session, &pdtid, cmd);
       if (status != PTHDB_SUCCESS || pdtid == PTHDB_INVALID_PTHREAD)
 	break;
 
-      status = pthdb_pthread_ptid (pd_session, pdtid, &pthid);
+      status = pthdb_pthread_ptid (tmap[pid].pd_session, pdtid, &pthid);
       if (status != PTHDB_SUCCESS || pthid == PTHDB_INVALID_PTID)
 	continue;
 
@@ -780,7 +786,7 @@ sync_threadlists (int pid)
 
   for (pi = 0; pi < pcount; pi++)
     {
-      status = pthdb_pthread_tid (pd_session, pbuf[pi].pdtid, &tid);
+      status = pthdb_pthread_tid (tmap[pid].pd_session, pbuf[pi].pdtid, &tid);
       if (status != PTHDB_SUCCESS)
 	tid = PTHDB_INVALID_TID;
       pbuf[pi].tid = tid;
@@ -790,10 +796,11 @@ sync_threadlists (int pid)
 
   /* Accumulate an array of GDB threads sorted by pid.  */
 
-  gcount = 0;
-  iterate_over_threads (giter_count, &gcount);
+  /* gcount is GDB thread count and pcount is pthreadlib thread count.  */
+
+  gcount = giter_count (pid);
   g = gbuf = XNEWVEC (struct thread_info *, gcount);
-  iterate_over_threads (giter_accum, &g);
+  giter_accum (&g, pid);
   qsort (gbuf, gcount, sizeof *gbuf, gcmp);
 
   /* Apply differences between the two arrays to GDB's thread list.  */
@@ -810,8 +817,6 @@ sync_threadlists (int pid)
 	  priv->pdtid = pbuf[pi].pdtid;
 	  priv->tid = pbuf[pi].tid;
 
-	  process_stratum_target *proc_target
-	    = current_inferior ()->process_target ();
 	  thread = add_thread_with_info (proc_target,
 					 ptid_t (pid, 0, pbuf[pi].pthid),
 					 priv);
@@ -830,6 +835,23 @@ sync_threadlists (int pid)
 
 	  cmp_result = ptid_cmp (pptid, gptid);
 
+	  /* If there is only one thread then we need not make the main 
+	     thread look like a thread.  It can stay as a process. This
+	     is useful when we have multiple inferiors, but only one is
+	     threaded.  So we need not make the other inferiors with only
+	     main thread, look like a threaded one.  For example, Thread
+	     1.1, 1.2, 2.1, 3.1 exists then it is useful to skip this for
+	     loop for 2.1 and 3.1 leaving them as main process thread with
+	     a dummy priv set.  */
+
+	  if (pcount == 1 && gcount == 1)
+	  {
+	    aix_thread_info *priv = new aix_thread_info;
+	    tp = find_thread_ptid (proc_target, gptid);
+	    tp->priv.reset (priv);
+	    break;
+	  }
+
 	  if (cmp_result == 0)
 	    {
 	      aix_thread_info *priv = get_aix_thread_info (gbuf[gi]);
@@ -841,8 +863,25 @@ sync_threadlists (int pid)
 	    }
 	  else if (cmp_result > 0)
 	    {
-	      delete_thread (gbuf[gi]);
-	      gi++;
+	      /* This is to make the main process thread now look
+		 like a thread.  */
+
+	      if (gptid.is_pid () && gptid.pid () == pptid.pid ())
+	      {
+		thread_change_ptid (proc_target, gptid, pptid);
+		aix_thread_info *priv = new aix_thread_info;
+		priv->pdtid = pbuf[pi].pdtid;
+		priv->tid = pbuf[pi].tid;
+		tp = find_thread_ptid (proc_target, pptid);
+		tp->priv.reset (priv);
+		pi++;
+		gi++;
+	      }
+	      else
+	      {
+		delete_thread (gbuf[gi]);
+		gi++;
+	      }
 	    }
 	  else
 	    {
@@ -888,10 +927,10 @@ pd_update (int pid)
   pthdb_tid_t tid;
   struct thread_info *thread = NULL;
 
-  if (!pd_active)
+  if (!tmap[pid].pd_active)
     return ptid_t (pid);
 
-  status = pthdb_session_update (pd_session);
+  status = pthdb_session_update (tmap[pid].pd_session);
   if (status != PTHDB_SUCCESS)
     return ptid_t (pid);
 
@@ -921,12 +960,12 @@ pd_activate (int pid)
 		
   status = pthdb_session_init (pid, arch64 ? PEM_64BIT : PEM_32BIT,
 			       PTHDB_FLAG_REGS, &pd_callbacks, 
-			       &pd_session);
+			       &tmap[pid].pd_session);
   if (status != PTHDB_SUCCESS)
     {
       return ptid_t (pid);
     }
-  pd_active = 1;
+  tmap[pid].pd_active = 1;
   return pd_update (pid);
 }
 
@@ -935,12 +974,12 @@ pd_activate (int pid)
 static void
 pd_deactivate (void)
 {
-  if (!pd_active)
+  if (!tmap[inferior_ptid.pid ()].pd_active)
     return;
-  pthdb_session_destroy (pd_session);
+  pthdb_session_destroy (tmap[inferior_ptid.pid ()].pd_session);
   
   pid_to_prc (&inferior_ptid);
-  pd_active = 0;
+  tmap[inferior_ptid.pid ()].pd_active = 0;
 }
 
 /* An object file has just been loaded.  Check whether the current
@@ -953,8 +992,15 @@ pd_enable (void)
   char *stub_name;
   struct bound_minimal_symbol ms;
 
+  /* Create set of variables for this inferior.  */
+  if (tmap.find (inferior_ptid.pid ()) == tmap.end ())
+  {
+    struct aix_thread_variables z = {0, 0};
+    tmap.insert ({inferior_ptid.pid (), z});
+  }
+
   /* Don't initialize twice.  */
-  if (pd_able)
+  if (tmap[inferior_ptid.pid ()].pd_able)
     return;
 
   /* Check application word size.  */
@@ -978,7 +1024,7 @@ pd_enable (void)
 
   /* Prepare for thread debugging.  */
   current_inferior ()->push_target (&aix_thread_ops);
-  pd_able = 1;
+  tmap[inferior_ptid.pid ()].pd_able = 1; 
 
   /* If we're debugging a core file or an attached inferior, the
      pthread library may already have been initialized, so try to
@@ -991,28 +1037,25 @@ pd_enable (void)
 static void
 pd_disable (void)
 {
-  if (!pd_able)
+  if (!tmap[inferior_ptid.pid ()].pd_able)
     return;
-  if (pd_active)
+  if (tmap[inferior_ptid.pid ()].pd_active)
     pd_deactivate ();
-  pd_able = 0;
+  tmap[inferior_ptid.pid ()].pd_able = 0;
   current_inferior ()->unpush_target (&aix_thread_ops);
+  tmap.erase (inferior_ptid.pid ());
 }
 
 /* new_objfile observer callback.
 
    If OBJFILE is non-null, check whether a threaded application is
-   being debugged, and if so, prepare for thread debugging.
-
-   If OBJFILE is null, stop debugging threads.  */
+   being debugged, and if so, prepare for thread debugging.  */
 
 static void
 new_objfile (struct objfile *objfile)
 {
   if (objfile)
     pd_enable ();
-  else
-    pd_disable ();
 }
 
 /* Attach to process specified by ARGS.  */
@@ -1096,7 +1139,7 @@ aix_thread_target::wait (ptid_t ptid, struct target_waitstatus *status,
   gdb_assert (ptid.is_pid ());
 
   /* Check whether libpthdebug might be ready to be initialized.  */
-  if (!pd_active && status->kind () == TARGET_WAITKIND_STOPPED
+  if (!tmap[ptid.pid ()].pd_active && status->kind () == TARGET_WAITKIND_STOPPED
       && status->sig () == GDB_SIGNAL_TRAP)
     {
       process_stratum_target *proc_target
@@ -1233,7 +1276,7 @@ fetch_regs_user_thread (struct regcache *regcache, pthdb_pthread_t pdtid)
   if (debug_aix_thread)
     gdb_printf (gdb_stdlog, 
 		"fetch_regs_user_thread %lx\n", (long) pdtid);
-  status = pthdb_pthread_context (pd_session, pdtid, &ctx);
+  status = pthdb_pthread_context (tmap[inferior_ptid.pid ()].pd_session, pdtid, &ctx);
   if (status != PTHDB_SUCCESS)
     error (_("aix-thread: fetch_registers: pthdb_pthread_context returned %s"),
 	   pd_status2str (status));
@@ -1518,7 +1561,7 @@ store_regs_user_thread (const struct regcache *regcache, pthdb_pthread_t pdtid)
 
   /* Retrieve the thread's current context for its non-register
      values.  */
-  status = pthdb_pthread_context (pd_session, pdtid, &ctx);
+  status = pthdb_pthread_context (tmap[inferior_ptid.pid ()].pd_session, pdtid, &ctx);
   if (status != PTHDB_SUCCESS)
     error (_("aix-thread: store_registers: pthdb_pthread_context returned %s"),
 	   pd_status2str (status));
@@ -1576,7 +1619,7 @@ store_regs_user_thread (const struct regcache *regcache, pthdb_pthread_t pdtid)
 	ctx.fpscr = tmp_fpscr;
     }
 
-  status = pthdb_pthread_setcontext (pd_session, pdtid, &ctx);
+  status = pthdb_pthread_setcontext (tmap[inferior_ptid.pid ()].pd_session, pdtid, &ctx);
   if (status != PTHDB_SUCCESS)
     error (_("aix-thread: store_registers: "
 	     "pthdb_pthread_setcontext returned %s"),
@@ -1741,7 +1784,7 @@ aix_thread_target::mourn_inferior ()
 {
   target_ops *beneath = this->beneath ();
 
-  pd_deactivate ();
+  pd_disable ();
   beneath->mourn_inferior ();
 }
 
@@ -1800,24 +1843,24 @@ aix_thread_target::extra_thread_info (struct thread_info *thread)
     /* i18n: Like "thread-identifier %d, [state] running, suspended" */
     buf.printf (_("tid %d"), (int)tid);
 
-  status = pthdb_pthread_state (pd_session, pdtid, &state);
+  status = pthdb_pthread_state (tmap[inferior_ptid.pid ()].pd_session, pdtid, &state);
   if (status != PTHDB_SUCCESS)
     state = PST_NOTSUP;
   buf.printf (", %s", state2str (state));
 
-  status = pthdb_pthread_suspendstate (pd_session, pdtid, 
+  status = pthdb_pthread_suspendstate (tmap[inferior_ptid.pid ()].pd_session, pdtid, 
 				       &suspendstate);
   if (status == PTHDB_SUCCESS && suspendstate == PSS_SUSPENDED)
     /* i18n: Like "Thread-Id %d, [state] running, suspended" */
     buf.printf (_(", suspended"));
 
-  status = pthdb_pthread_detachstate (pd_session, pdtid, 
+  status = pthdb_pthread_detachstate (tmap[inferior_ptid.pid ()].pd_session, pdtid, 
 				      &detachstate);
   if (status == PTHDB_SUCCESS && detachstate == PDS_DETACHED)
     /* i18n: Like "Thread-Id %d, [state] running, detached" */
     buf.printf (_(", detached"));
 
-  pthdb_pthread_cancelpend (pd_session, pdtid, &cancelpend);
+  pthdb_pthread_cancelpend (tmap[inferior_ptid.pid ()].pd_session, pdtid, &cancelpend);
   if (status == PTHDB_SUCCESS && cancelpend)
     /* i18n: Like "Thread-Id %d, [state] running, cancel pending" */
     buf.printf (_(", cancel pending"));
diff --git a/gdb/rs6000-aix-nat.c b/gdb/rs6000-aix-nat.c
index 2ac1f6e70b6..d1fece6faa7 100644
--- a/gdb/rs6000-aix-nat.c
+++ b/gdb/rs6000-aix-nat.c
@@ -95,6 +95,8 @@ class rs6000_nat_target final : public inf_ptrace_target
 
   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
 
+  std::string pid_to_str (ptid_t) override;
+
   /* Fork detection related functions, For adding multi process debugging
      support.  */
   void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) override;
@@ -619,6 +621,14 @@ rs6000_nat_target::xfer_partial (enum target_object object,
     }
 }
 
+std::string 
+rs6000_nat_target::pid_to_str (ptid_t ptid)
+{
+  if (!ptid.tid ())
+    return string_printf (_("Process %s"), pulongest (ptid.pid ())); 
+
+  return string_printf (_("Thread %s"), pulongest (ptid.tid ()));
+}
 /* Wait for the child specified by PTID to do something.  Return the
    process ID of the child, or MINUS_ONE_PTID in case of error; store
    the status in *OURSTATUS.  */
diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
index f483f54de13..74676cbfa50 100644
--- a/gdb/solib-aix.c
+++ b/gdb/solib-aix.c
@@ -550,6 +550,10 @@ solib_aix_in_dynsym_resolve_code (CORE_ADDR pc)
   return 0;
 }
 
+/* For multi inferiors, post object file name change
+   we store the new names in this vector.  */
+std::vector<std::string> aix_slib_name;
+
 /* Implement the "bfd_open" target_so_ops method.  */
 
 static gdb_bfd_ref_ptr
@@ -618,6 +622,16 @@ solib_aix_bfd_open (const char *pathname)
       if (member_name == bfd_get_filename (object_bfd.get ()))
 	break;
 
+      std::string s = bfd_get_filename (object_bfd.get ());
+      auto it = aix_slib_name.begin ();
+      while (it != aix_slib_name.end ())
+      {
+	std::string s1 = *it;
+	if (s1.compare(s) == 0)
+	  return object_bfd;
+	it++;
+      }
+
       object_bfd = gdb_bfd_openr_next_archived_file (archive_bfd.get (),
 						     object_bfd.get ());
     }
@@ -644,6 +658,7 @@ solib_aix_bfd_open (const char *pathname)
   std::string fname = string_printf ("%s%s",
 				     bfd_get_filename (archive_bfd.get ()),
 				     sep);
+  aix_slib_name.push_back (fname);
   bfd_set_filename (object_bfd.get (), fname.c_str ());
 
   return object_bfd;
-- 
2.31.1

