From: Guinevere Larsen Date: Thu, 30 Oct 2025 20:43:55 +0000 (-0300) Subject: gdb/record_full: add ptid entry for history. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b8524f61e398072f83938fb805f377f22198f9a9;p=thirdparty%2Fbinutils-gdb.git gdb/record_full: add ptid entry for history. 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 --- diff --git a/gdb/infrun.c b/gdb/infrun.c index bd114e16b80..b76f23b2e7b 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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. diff --git a/gdb/record-full.c b/gdb/record-full.c index 4c541a64037..9fb9f632796 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -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; }