]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/record_full: add ptid entry for history.
authorGuinevere Larsen <guinevere@redhat.com>
Thu, 30 Oct 2025 20:43:55 +0000 (17:43 -0300)
committerGuinevere Larsen <guinevere@redhat.com>
Mon, 8 Dec 2025 11:46:11 +0000 (08:46 -0300)
For record full to work on multi-threaded inferiors, we must know to
which thread any given recorded instruction belongs to. This commit adds
this as a new entry for each instruction when an inferior is
multi-threaded. This way, when replaying the instruction, the subsystem
is able to change inferior thread as needed, and avoid SIGILLs when
changing stuff

Ideally, we would only add the PTID entry when we actually need to
switch threads, but that is left as a future improvement.

WIP: replaying doesn't automatically change threads yet, but there was
an attempt

gdb/infrun.c
gdb/record-full.c

index bd114e16b80ea1db53d561fb744b8cff2c9afeae..b76f23b2e7bc95e771e809d9c0cd0661f96ae6b0 100644 (file)
@@ -5742,6 +5742,13 @@ stop_all_threads (const char *reason, inferior *inf)
              if (!target_is_non_stop_p ())
                continue;
 
+             /* If this thread is being replayed by the record-full
+                subsystem, there won't be any need to manually stop
+                the thread, since recording already serializes them,
+                making them execute one at a time.  */
+             if (target_record_is_replaying (t.ptid))
+               continue;
+
              if (t.executing ())
                {
                  /* If already stopping, don't request a stop again.
index 4c541a6403719c605773c8adb7f077d155b6858f..9fb9f632796b3b5c20b4c2580e041be2d1e913fe 100644 (file)
@@ -121,7 +121,8 @@ enum record_full_type
 {
   record_full_end = 0,
   record_full_reg,
-  record_full_mem
+  record_full_mem,
+  record_full_ptid,
 };
 
 /* This is the data structure that makes up the execution log.
@@ -161,6 +162,8 @@ struct record_full_entry
     struct record_full_mem_entry mem;
     /* end */
     struct record_full_end_entry end;
+
+    ptid_t ptid;
   } u;
 };
 
@@ -441,6 +444,20 @@ record_full_mem_alloc (CORE_ADDR addr, int len)
   return rec;
 }
 
+/* Allocate a record_full_ptid record entry.  */
+
+static inline struct record_full_entry *
+record_full_ptid_alloc (ptid_t ptid)
+{
+  struct record_full_entry *rec;
+
+  rec = XCNEW (struct record_full_entry);
+  rec->type = record_full_ptid;
+  rec->u.ptid = ptid;
+
+  return rec;
+}
+
 /* Free a record_full_mem record entry.  */
 
 static inline void
@@ -473,6 +490,12 @@ record_full_end_release (struct record_full_entry *rec)
   xfree (rec);
 }
 
+static inline void
+record_full_ptid_release (struct record_full_entry *rec)
+{
+  xfree (rec);
+}
+
 /* Free one record entry, any type.
    Return entry->type, in case caller wants to know.  */
 
@@ -491,6 +514,11 @@ record_full_entry_release (struct record_full_entry *rec)
   case record_full_end:
     record_full_end_release (rec);
     break;
+  case record_full_ptid:
+    record_full_ptid_release (rec);
+    break;
+  default:
+    gdb_assert_not_reached ("unhandled record_full_entry type");
   }
   return type;
 }
@@ -613,6 +641,7 @@ record_full_get_loc (struct record_full_entry *rec)
     else
       return rec->u.reg.u.buf;
   case record_full_end:
+  case record_full_ptid:
   default:
     gdb_assert_not_reached ("unexpected record_full_entry type");
     return NULL;
@@ -672,6 +701,24 @@ record_full_arch_list_add_mem (CORE_ADDR addr, int len)
   return 0;
 }
 
+/* Add a PTID entry to the history, marking that the replay should
+   switch threads.  */
+
+static bool
+record_full_arch_list_add_ptid (ptid_t ptid)
+{
+  struct record_full_entry *rec;
+
+  if (record_debug > 1)
+    gdb_printf (gdb_stdlog,
+               "Process record: add ptid to "
+               "record list.\n");
+
+  rec = record_full_ptid_alloc (ptid);
+  record_full_arch_list_add (rec);
+  return true;
+}
+
 /* Add a record_full_end type struct record_full_entry to
    record_full_arch_list.  */
 
@@ -714,10 +761,12 @@ record_full_check_insn_num (void)
    only can step), GDB will call this function to record the values to
    record_full_list.  This function will call gdbarch_process_record to
    record the running message of inferior and set them to
-   record_full_arch_list, and add it to record_full_list.  */
+   record_full_arch_list, and add it to record_full_list.
+   PTID contains the PID and TID of the instruction being recorded.  */
 
 static void
-record_full_message (struct regcache *regcache, enum gdb_signal signal)
+record_full_message (struct regcache *regcache, enum gdb_signal signal,
+                    ptid_t ptid)
 {
   int ret;
   struct gdbarch *gdbarch = regcache->arch ();
@@ -730,6 +779,11 @@ record_full_message (struct regcache *regcache, enum gdb_signal signal)
       /* Check record_full_insn_num.  */
       record_full_check_insn_num ();
 
+      /* If there is more than one thread, the history should know which
+        thread the current instruction belongs to.  */
+      if (thread_count (nullptr) > 1)
+       record_full_arch_list_add_ptid (ptid);
+
       /* If gdb sends a signal value to target_resume,
         save it in the 'end' field of the previous instruction.
 
@@ -793,11 +847,11 @@ record_full_message (struct regcache *regcache, enum gdb_signal signal)
 
 static bool
 record_full_message_wrapper_safe (struct regcache *regcache,
-                                 enum gdb_signal signal)
+                                 enum gdb_signal signal, ptid_t ptid)
 {
   try
     {
-      record_full_message (regcache, signal);
+      record_full_message (regcache, signal, ptid);
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1088,7 +1142,7 @@ record_full_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
       struct regcache *regcache = get_thread_regcache (inferior_thread ());
       struct gdbarch *gdbarch = regcache->arch ();
 
-      record_full_message (regcache, signal);
+      record_full_message (regcache, signal, ptid);
 
       if (!step)
        {
@@ -1191,7 +1245,6 @@ record_full_wait_1 (struct target_ops *ops,
     {
       if (record_full_resume_step)
        {
-         /* This is a single step.  */
          return ops->beneath ()->wait (ptid, status, options);
        }
       else
@@ -1259,7 +1312,8 @@ record_full_wait_1 (struct target_ops *ops,
                      int step = 1;
 
                      if (!record_full_message_wrapper_safe (regcache,
-                                                            GDB_SIGNAL_0))
+                                                            GDB_SIGNAL_0,
+                                                            ptid))
                        {
                           status->set_stopped (GDB_SIGNAL_0);
                           break;
@@ -1304,6 +1358,7 @@ record_full_wait_1 (struct target_ops *ops,
     }
   else
     {
+      bool multi_thread = (thread_count (nullptr) > 1);
       switch_to_thread (current_inferior ()->process_target (),
                        record_full_resume_ptid);
       regcache *regcache = get_thread_regcache (inferior_thread ());
@@ -1366,6 +1421,44 @@ record_full_wait_1 (struct target_ops *ops,
                  break;
                }
 
+             if (multi_thread)
+               {
+                 if (execution_direction == EXEC_REVERSE)
+                   {
+                     record_full_entry *pid_entry = record_full_list;
+                     while (pid_entry->type != record_full_end
+                            && pid_entry->type != record_full_ptid
+                            && pid_entry->prev != nullptr)
+                       pid_entry = pid_entry->prev;
+                     /* If we couldn't find the ptid,
+                        there's no need to change threads.  */
+                     if (pid_entry->type == record_full_ptid)
+                       {
+                         switch_to_thread
+                           (current_inferior ()->process_target (),
+                            pid_entry->u.ptid);
+                         regcache = get_thread_regcache (inferior_thread ());
+                       }
+                   }
+                 else
+                   {
+                     record_full_entry *pid_entry = record_full_list;
+                     while (pid_entry->type != record_full_end
+                            && pid_entry->type != record_full_ptid
+                            && pid_entry->next != nullptr)
+                       pid_entry = pid_entry->next;
+                     /* If we couldn't find the ptid,
+                        there's no need to change threads.  */
+                     if (pid_entry->type == record_full_ptid)
+                       {
+                         switch_to_thread
+                           (current_inferior ()->process_target (),
+                            pid_entry->u.ptid);
+                         regcache = get_thread_regcache (inferior_thread ());
+                       }
+                   }
+               }
+
              record_full_exec_insn (regcache, gdbarch, record_full_list);
 
              if (record_full_list->type == record_full_end)
@@ -2864,6 +2957,12 @@ maintenance_print_record_instruction (const char *args, int from_tty)
              gdb_printf ("\n");
              break;
            }
+         case record_full_ptid:
+           gdb_printf ("Instruction referencing thread %s\n",
+                       to_print->u.ptid.to_string ().c_str ());
+           break;
+         default:
+           gdb_assert_not_reached ("Unhandled record type");
        }
       to_print = to_print->next;
     }