* [RFC] Prec multi-thread support
@ 2009-11-02 16:45 Hui Zhu
2009-11-02 18:14 ` Michael Snyder
2009-11-09 18:00 ` Tom Tromey
0 siblings, 2 replies; 5+ messages in thread
From: Hui Zhu @ 2009-11-02 16:45 UTC (permalink / raw)
To: gdb-patches ml; +Cc: Michael Snyder
[-- Attachment #1: Type: text/plain, Size: 96 bytes --]
Hi guys,
This patches just for discussions and comments. Please help me with it.
Thanks,
Hui
[-- Attachment #2: prec-fix-error-handler.txt --]
[-- Type: text/plain, Size: 1797 bytes --]
---
record.c | 29 ++++++++++-------------------
1 file changed, 10 insertions(+), 19 deletions(-)
--- a/record.c
+++ b/record.c
@@ -954,7 +954,6 @@ record_close (int quitting)
}
static int record_resume_step = 0;
-static int record_resume_error;
/* "to_resume" target method. Resume the process record target. */
@@ -966,15 +965,11 @@ record_resume (struct target_ops *ops, p
if (!RECORD_IS_REPLAY)
{
- if (do_record_message (get_current_regcache (), signal))
- {
- record_resume_error = 0;
- }
- else
- {
- record_resume_error = 1;
- return;
- }
+ struct record_message_args args;
+
+ args.regcache = get_current_regcache ();
+ args.signal = signal;
+ record_message (&args);
record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
signal);
}
@@ -1038,14 +1033,6 @@ record_wait (struct target_ops *ops,
if (!RECORD_IS_REPLAY && ops != &record_core_ops)
{
- if (record_resume_error)
- {
- /* If record_resume get error, return directly. */
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = TARGET_SIGNAL_ABRT;
- return inferior_ptid;
- }
-
if (record_resume_step)
{
/* This is a single step. */
@@ -1091,7 +1078,11 @@ record_wait (struct target_ops *ops,
Therefore we will not return to gdb.
Record the insn and resume. */
if (!do_record_message (regcache, TARGET_SIGNAL_0))
- break;
+ {
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_0;
+ break;
+ }
record_beneath_to_resume (record_beneath_to_resume_ops,
ptid, 1,
[-- Attachment #3: prec-x86-add-insn.txt --]
[-- Type: text/plain, Size: 734 bytes --]
---
i386-tdep.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -4847,9 +4847,6 @@ reswitch:
/* int3 */
/* XXX */
case 0xcc:
- printf_unfiltered (_("Process record doesn't support instruction "
- "int3.\n"));
- ir.addr -= 1;
goto no_support;
break;
@@ -4958,10 +4955,8 @@ reswitch:
/* rdtsc */
case 0x0f31:
- printf_unfiltered (_("Process record doesn't support "
- "instruction rdtsc.\n"));
- ir.addr -= 2;
- goto no_support;
+ I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM);
+ I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM);
break;
/* sysenter */
[-- Attachment #4: prec-thread.txt --]
[-- Type: text/plain, Size: 33715 bytes --]
---
amd64-linux-tdep.c | 4
gdbthread.h | 2
i386-linux-tdep.c | 4
i386-tdep.c | 52 ++--
i386-tdep.h | 6
linux-record.c | 7
linux-record.h | 2
record.c | 566 ++++++++++++++++++++++++++++++++++++++++++++---------
record.h | 1
9 files changed, 516 insertions(+), 128 deletions(-)
--- a/amd64-linux-tdep.c
+++ b/amd64-linux-tdep.c
@@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li
#define RECORD_ARCH_GET_GS 0x1004
static int
-amd64_linux_syscall_record (struct regcache *regcache)
+amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr)
{
int ret;
ULONGEST syscall_native;
@@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca
}
else
{
- ret = record_linux_system_call (syscall_gdb, regcache,
+ ret = record_linux_system_call (syscall_gdb, addr, regcache,
&amd64_linux_record_tdep);
if (ret)
return ret;
--- a/gdbthread.h
+++ b/gdbthread.h
@@ -37,6 +37,8 @@ struct thread_info
kernel thread id, etc. */
int num; /* Convenient handle (GDB thread id) */
+ int record_is_waiting;
+
/* Non-zero means the thread is executing. Note: this is different
from saying that there is an active target and we are stopped at
a breakpoint, for instance. This is a real indicator whether the
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall)
static struct linux_record_tdep i386_linux_record_tdep;
static int
-i386_linux_intx80_sysenter_record (struct regcache *regcache)
+i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr)
{
int ret;
LONGEST syscall_native;
@@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc
return 0;
}
- ret = record_linux_system_call (syscall_gdb, regcache,
+ ret = record_linux_system_call (syscall_gdb, addr, regcache,
&i386_linux_record_tdep);
if (ret)
return ret;
--- a/i386-tdep.c
+++ b/i386-tdep.c
@@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor
if (irp->override >= 0)
{
- warning (_("Process record ignores the memory change "
- "of instruction at address %s because it "
- "can't get the value of the segment register."),
- paddress (gdbarch, irp->orig_addr));
+ if (record_debug)
+ warning (_("Process record ignores the memory change "
+ "of instruction at address %s because it "
+ "can't get the value of the segment register."),
+ paddress (gdbarch, irp->orig_addr));
return 0;
}
@@ -4042,11 +4043,12 @@ reswitch:
case 0xa3:
if (ir.override >= 0)
{
- warning (_("Process record ignores the memory change "
- "of instruction at address %s because "
- "it can't get the value of the segment "
- "register."),
- paddress (gdbarch, ir.orig_addr));
+ if (record_debug)
+ warning (_("Process record ignores the memory change "
+ "of instruction at address %s because "
+ "it can't get the value of the segment "
+ "register."),
+ paddress (gdbarch, ir.orig_addr));
}
else
{
@@ -4467,11 +4469,12 @@ reswitch:
if (ir.aflag && (es != ds))
{
/* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */
- warning (_("Process record ignores the memory "
- "change of instruction at address %s "
- "because it can't get the value of the "
- "ES segment register."),
- paddress (gdbarch, ir.orig_addr));
+ if (record_debug)
+ warning (_("Process record ignores the memory "
+ "change of instruction at address %s "
+ "because it can't get the value of the "
+ "ES segment register."),
+ paddress (gdbarch, ir.orig_addr));
}
else
{
@@ -4873,7 +4876,7 @@ reswitch:
ir.addr -= 2;
goto no_support;
}
- ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache);
+ ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr);
if (ret)
return ret;
}
@@ -4975,7 +4978,8 @@ reswitch:
ir.addr -= 2;
goto no_support;
}
- ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache);
+ ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache,
+ ir.addr);
if (ret)
return ret;
}
@@ -5000,7 +5004,8 @@ reswitch:
ir.addr -= 2;
goto no_support;
}
- ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache);
+ ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache,
+ ir.addr);
if (ret)
return ret;
}
@@ -5136,12 +5141,13 @@ reswitch:
/* sidt */
if (ir.override >= 0)
{
- warning (_("Process record ignores the memory "
- "change of instruction at "
- "address %s because it can't get "
- "the value of the segment "
- "register."),
- paddress (gdbarch, ir.orig_addr));
+ if (record_debug)
+ warning (_("Process record ignores the memory "
+ "change of instruction at "
+ "address %s because it can't get "
+ "the value of the segment "
+ "register."),
+ paddress (gdbarch, ir.orig_addr));
}
else
{
--- a/i386-tdep.h
+++ b/i386-tdep.h
@@ -115,11 +115,11 @@ struct gdbarch_tdep
in GDB is not same as I386 instructions. */
const int *record_regmap;
/* Parse intx80 args. */
- int (*i386_intx80_record) (struct regcache *regcache);
+ int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr);
/* Parse sysenter args. */
- int (*i386_sysenter_record) (struct regcache *regcache);
+ int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr);
/* Parse syscall args. */
- int (*i386_syscall_record) (struct regcache *regcache);
+ int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr);
};
/* Floating-point registers. */
--- a/linux-record.c
+++ b/linux-record.c
@@ -222,7 +222,7 @@ record_linux_msghdr (struct regcache *re
Return -1 if something wrong. */
int
-record_linux_system_call (enum gdb_syscall syscall,
+record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr,
struct regcache *regcache,
struct linux_record_tdep *tdep)
{
@@ -1209,10 +1209,13 @@ record_linux_system_call (enum gdb_sysca
case gdb_sys_fsync:
case gdb_sys_sigreturn:
- case gdb_sys_clone:
case gdb_sys_setdomainname:
break;
+ case gdb_sys_clone:
+ record_step = 0;
+ break;
+
case gdb_sys_newuname:
regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest);
if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest,
--- a/linux-record.h
+++ b/linux-record.h
@@ -534,7 +534,7 @@ enum gdb_syscall {
/* Record a linux syscall. */
-extern int record_linux_system_call (enum gdb_syscall num,
+extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr,
struct regcache *regcache,
struct linux_record_tdep *tdep);
#endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -30,6 +30,7 @@
#include "record.h"
#include "elf-bfd.h"
#include "gcore.h"
+#include "observer.h"
#include <signal.h>
@@ -60,6 +61,8 @@
#define RECORD_FILE_MAGIC netorder32(0x20091016)
+#define RECORD_THREAD (record_ops.beneath->to_stratum == thread_stratum)
+
/* These are the core structs of the process record functionality.
A record_entry is a record of the value change of a register
@@ -106,7 +109,8 @@ enum record_type
{
record_end = 0,
record_reg,
- record_mem
+ record_mem,
+ record_ptid
};
/* This is the data structure that makes up the execution log.
@@ -144,6 +148,8 @@ struct record_entry
struct record_reg_entry reg;
/* mem */
struct record_mem_entry mem;
+ /* ptid */
+ ptid_t ptid;
/* end */
struct record_end_entry end;
} u;
@@ -196,10 +202,18 @@ static int record_insn_num = 0;
than count of insns presently in execution log). */
static ULONGEST record_insn_count;
+/* In record mode, it's the step of next resume. */
+int record_step;
+
+/* The current inferior_ptid that is recorded. */
+static ptid_t record_prev_ptid;
+
/* The target_ops of process record. */
static struct target_ops record_ops;
static struct target_ops record_core_ops;
+static struct observer *record_threads_observer = NULL;
+
/* The beneath function pointers. */
static struct target_ops *record_beneath_to_resume_ops;
static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
@@ -285,6 +299,27 @@ record_mem_release (struct record_entry
xfree (rec);
}
+/* Alloc a record_ptid record entry. */
+
+static inline struct record_entry *
+record_ptid_alloc (void)
+{
+ struct record_entry *rec;
+
+ rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+ rec->type = record_ptid;
+
+ return rec;
+}
+
+/* Free a record_ptid record entry. */
+
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+ xfree (rec);
+}
+
/* Alloc a record_end record entry. */
static inline struct record_entry *
@@ -321,6 +356,9 @@ record_entry_release (struct record_entr
case record_mem:
record_mem_release (rec);
break;
+ case record_ptid:
+ record_ptid_release (rec);
+ break;
case record_end:
record_end_release (rec);
break;
@@ -509,6 +547,24 @@ record_arch_list_add_mem (CORE_ADDR addr
return 0;
}
+static void
+record_arch_list_add_ptid (ptid_t *ptid)
+{
+ struct record_entry *rec;
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: add ptid = %s to "
+ "record list.\n",
+ target_pid_to_str (*ptid));
+
+ rec = record_ptid_alloc ();
+
+ rec->u.ptid = *ptid;
+
+ record_arch_list_add (rec);
+}
+
/* Add a record_end type struct record_entry to record_arch_list. */
int
@@ -570,7 +626,6 @@ record_arch_list_cleanups (void *ignore)
record_arch_list, and add it to record_list. */
struct record_message_args {
- struct regcache *regcache;
enum target_signal signal;
};
@@ -579,15 +634,21 @@ record_message (void *args)
{
int ret;
struct record_message_args *myargs = args;
- struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
+ record_step = 1;
+
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
/* Check record_insn_num. */
record_check_insn_num (1);
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+
/* If gdb sends a signal value to target_resume,
save it in the 'end' field of the previous instruction.
@@ -610,20 +671,45 @@ record_message (void *args)
But we should still deliver the signal to gdb during the replay,
if we delivered it during the recording. Therefore we should
record the signal during record_wait, not record_resume. */
- if (record_list != &record_first) /* FIXME better way to check */
+ if (ptid_equal (record_prev_ptid, inferior_ptid))
{
- gdb_assert (record_list->type == record_end);
- record_list->u.end.sigval = myargs->signal;
+ if (myargs->signal != TARGET_SIGNAL_0)
+ {
+ gdb_assert (record_list->type == record_end);
+ record_list->u.end.sigval = myargs->signal;
+ }
+ }
+ else
+ {
+ if (myargs->signal != TARGET_SIGNAL_0)
+ {
+ struct record_entry *rec;
+ for (rec = record_list; rec != record_first.next; rec = rec->prev)
+ {
+ if (rec->type == record_ptid
+ && ptid_equal (rec->u.ptid, inferior_ptid))
+ {
+ gdb_assert (rec->prev->type == record_end);
+ rec->prev->u.end.sigval = myargs->signal;
+ break;
+ }
+ }
+ }
+
+ /* If the inferior_ptid change (inferior_ptid is not same with
+ record_prev_ptid), record record_prev_ptid to record list. */
+ record_arch_list_add_ptid (&record_prev_ptid);
+ record_prev_ptid = inferior_ptid;
}
if (myargs->signal == TARGET_SIGNAL_0
|| !gdbarch_process_record_signal_p (gdbarch))
ret = gdbarch_process_record (gdbarch,
- myargs->regcache,
- regcache_read_pc (myargs->regcache));
+ regcache,
+ regcache_read_pc (regcache));
else
ret = gdbarch_process_record_signal (gdbarch,
- myargs->regcache,
+ regcache,
myargs->signal);
if (ret > 0)
@@ -646,12 +732,10 @@ record_message (void *args)
}
static int
-do_record_message (struct regcache *regcache,
- enum target_signal signal)
+do_record_message (enum target_signal signal)
{
struct record_message_args args;
- args.regcache = regcache;
args.signal = signal;
return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL);
}
@@ -678,9 +762,12 @@ record_gdb_operation_disable_set (void)
entries and memory entries, followed by an 'end' entry. */
static inline void
-record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
+record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p,
struct record_entry *entry)
{
+ struct regcache *regcache = *regcache_p;
+ struct gdbarch *gdbarch = *gdbarch_p;
+
switch (entry->type)
{
case record_reg: /* reg */
@@ -744,9 +831,59 @@ record_exec_insn (struct regcache *regca
}
}
break;
+
+ case record_ptid: /* ptid */
+ {
+ ptid_t tmp_ptid;
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_ptid %s to "
+ "inferior ptid = %s.\n",
+ host_address_to_string (entry),
+ target_pid_to_str (entry->u.ptid));
+
+ if (!ptid_equal (entry->u.ptid, null_ptid))
+ {
+ inferior_ptid = entry->u.ptid;
+ *regcache_p = get_current_regcache ();
+ *gdbarch_p = get_regcache_arch (*regcache_p);
+ }
+ tmp_ptid = entry->u.ptid;
+ entry->u.ptid = record_prev_ptid;
+ record_prev_ptid = tmp_ptid;
+ }
+ break;
}
}
+static int record_thread_number;
+
+static int
+record_thread_number_count (struct thread_info *thread, void *unused)
+{
+ record_thread_number ++;
+
+ return 0;
+}
+
+static void
+record_thread_number_reset (void)
+{
+ record_thread_number = 0;
+
+ if (RECORD_THREAD)
+ iterate_over_threads (record_thread_number_count, NULL);
+}
+
+static void
+record_new_thread_handler (struct thread_info *tp)
+{
+ tp->record_is_waiting = 0;
+
+ record_thread_number_reset ();
+}
+
static struct target_ops *tmp_to_resume_ops;
static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
enum target_signal);
@@ -838,6 +975,8 @@ record_open_1 (char *name, int from_tty)
error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
push_target (&record_ops);
+
+ record_threads_observer = observer_attach_new_thread (record_new_thread_handler);
}
/* "to_open" target method. Open the process record target. */
@@ -903,6 +1042,7 @@ record_open (char *name, int from_tty)
record_insn_count = 0;
record_list = &record_first;
record_list->next = NULL;
+ record_prev_ptid = null_ptid;
/* Set the tmp beneath pointers to beneath pointers. */
record_beneath_to_resume_ops = tmp_to_resume_ops;
@@ -920,6 +1060,8 @@ record_open (char *name, int from_tty)
record_core_open_1 (name, from_tty);
else
record_open_1 (name, from_tty);
+
+ record_thread_number_reset ();
}
/* "to_close" target method. Close the process record target. */
@@ -932,6 +1074,12 @@ record_close (int quitting)
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
+ if (record_threads_observer)
+ {
+ observer_detach_new_thread (record_threads_observer);
+ record_threads_observer = NULL;
+ }
+
record_list_release (record_list);
/* Release record_core_regbuf. */
@@ -954,6 +1102,8 @@ record_close (int quitting)
}
static int record_resume_step = 0;
+static ptid_t record_resume_ptid;
+static enum target_signal record_resume_signal = TARGET_SIGNAL_0;
/* "to_resume" target method. Resume the process record target. */
@@ -965,14 +1115,188 @@ record_resume (struct target_ops *ops, p
if (!RECORD_IS_REPLAY)
{
- struct record_message_args args;
+ record_resume_ptid = ptid;
- args.regcache = get_current_regcache ();
- args.signal = signal;
- record_message (&args);
- record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
- signal);
+ if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid))
+ {
+ record_resume_signal = signal;
+ }
+ else
+ {
+ struct record_message_args args;
+
+ args.signal = signal;
+ record_message (&args);
+ record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+ record_step, signal);
+ }
+ }
+}
+
+struct record_thread_wait_args {
+ ptid_t real_inferior_ptid;
+ struct thread_info *real_inferior_tp;
+ struct target_waitstatus real_inferior_status;
+
+ struct target_waitstatus *status;
+ int options;
+};
+
+static int
+record_thread_reset_callback (struct thread_info *tp, void *data)
+{
+ tp->record_is_waiting = 0;
+
+ return 0;
+}
+
+static int
+record_thread_check_callback (struct thread_info *tp, void *data)
+{
+ if (tp->record_is_waiting)
+ return 1;
+
+ return 0;
+}
+
+/* Return 1 if it is not a simple step.
+ Return 0 if it is a simple step. */
+
+static int
+record_thread_wait (ptid_t ptid, struct target_waitstatus *status,
+ int options, ptid_t *ret_ptid)
+{
+ ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+ ptid, status, options);
+
+ if (ret_ptid)
+ *ret_ptid = wait_ptid;
+
+ if (status->kind == TARGET_WAITKIND_IGNORE)
+ return 0;
+
+ inferior_ptid = wait_ptid;
+
+ /* Is this a SIGTRAP? */
+ if (status->kind == TARGET_WAITKIND_STOPPED
+ && status->value.sig == TARGET_SIGNAL_TRAP)
+ {
+ CORE_ADDR tmp_pc;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+
+ /* Yes -- check if there is a breakpoint. */
+ registers_changed ();
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+ tmp_pc = regcache_read_pc (regcache);
+ if (!record_step)
+ tmp_pc -= gdbarch_decr_pc_after_break (gdbarch);
+ if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc))
+ {
+ /* There is a breakpoint. GDB will want to stop. */
+ if (record_step)
+ {
+ CORE_ADDR decr_pc_after_break
+ = gdbarch_decr_pc_after_break (gdbarch);
+ if (decr_pc_after_break)
+ regcache_write_pc (regcache, tmp_pc + decr_pc_after_break);
+ }
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+record_thread_wait_callback (struct thread_info *tp, void *data)
+{
+ struct record_thread_wait_args *args = data;
+ enum target_signal sig = TARGET_SIGNAL_0;
+ int options = args->options;
+ int ret;
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_thread_wait_callback "
+ "resume %s\n", target_pid_to_str (tp->ptid));
+
+ inferior_ptid = tp->ptid;
+
+ if (!tp->record_is_waiting)
+ {
+ if (record_resume_signal != TARGET_SIGNAL_0
+ && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+ {
+ sig = record_resume_signal;
+ record_resume_signal = TARGET_SIGNAL_0;
+ }
+ if (!do_record_message (sig))
+ {
+ args->status->kind = TARGET_WAITKIND_STOPPED;
+ args->status->value.sig = TARGET_SIGNAL_0;
+ return 1;
+ }
+
+ if (record_step == 0)
+ {
+ /* The next insn is a sys_clone. */
+ if (iterate_over_threads (record_thread_check_callback, NULL))
+ target_stop (minus_one_ptid);
+ non_stop = 0;
+ }
+
+ record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid,
+ record_step, sig);
+ }
+ else
+ {
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_thread_wait_callback "
+ "not resume %s\n", target_pid_to_str (tp->ptid));
+ }
+
+ if (record_step)
+ options |= TARGET_WNOHANG;
+ ret = record_thread_wait (inferior_ptid, args->status,
+ options, NULL);
+ if (args->status->kind == TARGET_WAITKIND_IGNORE)
+ {
+ if (!tp->record_is_waiting)
+ {
+ tp->record_is_waiting = 1;
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_thread_wait_callback "
+ "start waiting %s.\n",
+ target_pid_to_str (tp->ptid));
+ }
+ }
+ else
+ {
+ if (tp->record_is_waiting)
+ {
+ tp->record_is_waiting = 0;
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_thread_wait_callback "
+ "stop waiting %s.\n",
+ target_pid_to_str (tp->ptid));
+ }
+
+ if (!args->real_inferior_tp
+ && ptid_equal (inferior_ptid, args->real_inferior_ptid))
+ args->real_inferior_tp = tp;
+ if (args->real_inferior_tp == tp)
+ args->real_inferior_status = *args->status;
}
+
+ return ret;
}
static int record_get_sig = 0;
@@ -994,7 +1318,13 @@ record_sig_handler (int signo)
}
static void
-record_wait_cleanups (void *ignore)
+record_wait_signal_cleanups (void *ignore)
+{
+ signal (SIGINT, handle_sigint);
+}
+
+static void
+record_wait_replay_cleanups (void *ignore)
{
if (execution_direction == EXEC_REVERSE)
{
@@ -1005,6 +1335,16 @@ record_wait_cleanups (void *ignore)
record_list = record_list->prev;
}
+static void
+record_wait_mthread_cleanups (void *ignore)
+{
+ /* Stop the threads that still running. */
+ if (iterate_over_threads (record_thread_check_callback, NULL))
+ target_stop (minus_one_ptid);
+
+ non_stop = 0;
+}
+
/* "to_wait" target method for process record target.
In record mode, the target is always run in singlestep mode
@@ -1023,7 +1363,10 @@ record_wait (struct target_ops *ops,
ptid_t ptid, struct target_waitstatus *status,
int options)
{
+ ptid_t ret_ptid;
struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+ struct cleanup *signal_cleanups
+ = make_cleanup (record_wait_signal_cleanups, 0);
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
@@ -1031,81 +1374,103 @@ record_wait (struct target_ops *ops,
"record_resume_step = %d\n",
record_resume_step);
+ record_get_sig = 0;
+ signal (SIGINT, record_sig_handler);
+
if (!RECORD_IS_REPLAY && ops != &record_core_ops)
{
- if (record_resume_step)
- {
- /* This is a single step. */
- return record_beneath_to_wait (record_beneath_to_wait_ops,
- ptid, status, options);
- }
- else
- {
- /* This is not a single step. */
- ptid_t ret;
- CORE_ADDR tmp_pc;
-
- while (1)
- {
- ret = record_beneath_to_wait (record_beneath_to_wait_ops,
- ptid, status, options);
+ /* Record mode. */
+ if (record_thread_number > 1 && !ptid_equal (record_resume_ptid,
+ inferior_ptid))
+ {
+ /* Multi-threads record. */
+ struct record_thread_wait_args args;
+ struct thread_info *tp;
+ struct cleanup *mthread_cleanups
+ = make_cleanup (record_wait_mthread_cleanups, 0);
- /* Is this a SIGTRAP? */
- if (status->kind == TARGET_WAITKIND_STOPPED
- && status->value.sig == TARGET_SIGNAL_TRAP)
- {
- struct regcache *regcache;
+ args.real_inferior_ptid = inferior_ptid;
+ args.real_inferior_tp = NULL;
+ args.status = status;
+ args.options = options;
+ non_stop = 1;
+ iterate_over_threads (record_thread_reset_callback, NULL);
- /* Yes -- check if there is a breakpoint. */
- registers_changed ();
- regcache = get_current_regcache ();
- tmp_pc = regcache_read_pc (regcache);
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
- tmp_pc))
- {
- /* There is a breakpoint. GDB will want to stop. */
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- CORE_ADDR decr_pc_after_break
- = gdbarch_decr_pc_after_break (gdbarch);
- if (decr_pc_after_break)
- regcache_write_pc (regcache,
- tmp_pc + decr_pc_after_break);
- }
- else
- {
- /* There is not a breakpoint, and gdb is not
- stepping, therefore gdb will not stop.
- Therefore we will not return to gdb.
- Record the insn and resume. */
- if (!do_record_message (regcache, TARGET_SIGNAL_0))
- {
- status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = TARGET_SIGNAL_0;
- break;
- }
+ while (1)
+ {
+ tp = iterate_over_threads (record_thread_wait_callback, &args);
+ if (tp)
+ break;
+ if (record_resume_step
+ && args.real_inferior_tp
+ && !args.real_inferior_tp->record_is_waiting)
+ {
+ *status = args.real_inferior_status;
+ inferior_ptid = args.real_inferior_ptid;
+ break;
+ }
+ }
- record_beneath_to_resume (record_beneath_to_resume_ops,
- ptid, 1,
- TARGET_SIGNAL_0);
- continue;
- }
- }
+ do_cleanups (mthread_cleanups);
- /* The inferior is broken by a breakpoint or a signal. */
- break;
+ ret_ptid = inferior_ptid;
+ }
+ else
+ {
+ /* Single-thread record. */
+ if (record_resume_step)
+ {
+ /* This is a single step. */
+ ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops,
+ ptid, status, options);
}
+ else
+ {
+ /* This is not a single step. */
+ while (1)
+ {
+ if (record_thread_wait (ptid, status,
+ options, &ret_ptid))
+ break;
- return ret;
- }
+ if (record_resume_step)
+ break;
+
+ /* There is not a breakpoint, and gdb is not
+ stepping, therefore gdb will not stop.
+ Therefore we will not return to gdb.
+ Record the insn and resume. */
+ if (!do_record_message (TARGET_SIGNAL_0))
+ {
+ ret_ptid = inferior_ptid;
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_0;
+ break;
+ }
+
+ record_beneath_to_resume (record_beneath_to_resume_ops,
+ record_resume_ptid, record_step,
+ TARGET_SIGNAL_0);
+ }
+ }
+ }
}
else
{
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ /* Replay mode. */
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
int continue_flag = 1;
int first_record_end = 1;
- struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+ struct cleanup *old_cleanups =
+ make_cleanup (record_wait_replay_cleanups, 0);
CORE_ADDR tmp_pc;
+ ptid_t old_inferior_ptid = inferior_ptid;
+
+ if (!ptid_equal (record_prev_ptid, null_ptid))
+ inferior_ptid = record_prev_ptid;
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
status->kind = TARGET_WAITKIND_STOPPED;
@@ -1129,8 +1494,6 @@ record_wait (struct target_ops *ops,
}
}
- record_get_sig = 0;
- signal (SIGINT, record_sig_handler);
/* If GDB is in terminal_inferior mode, it will not get the signal.
And in GDB replay mode, GDB doesn't need to be in terminal_inferior
mode, because inferior will not executed.
@@ -1161,7 +1524,7 @@ record_wait (struct target_ops *ops,
break;
}
- record_exec_insn (regcache, gdbarch, record_list);
+ record_exec_insn (®cache, &gdbarch, record_list);
if (record_list->type == record_end)
{
@@ -1184,12 +1547,13 @@ record_wait (struct target_ops *ops,
In EXEC_FORWARD mode, this is the record_end of current
instruction. */
/* step */
- if (record_resume_step)
+ if (record_resume_step
+ && ptid_equal (inferior_ptid, old_inferior_ptid))
{
if (record_debug > 1)
fprintf_unfiltered (gdb_stdlog,
"Process record: step.\n");
- continue_flag = 0;
+ continue_flag = 0;
}
/* check breakpoint */
@@ -1233,22 +1597,23 @@ record_wait (struct target_ops *ops,
}
while (continue_flag);
- signal (SIGINT, handle_sigint);
-
replay_out:
- if (record_get_sig)
- status->value.sig = TARGET_SIGNAL_INT;
- else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
+ if (record_list->u.end.sigval != TARGET_SIGNAL_0)
/* FIXME: better way to check */
status->value.sig = record_list->u.end.sigval;
else
status->value.sig = TARGET_SIGNAL_TRAP;
+ ret_ptid = inferior_ptid;
discard_cleanups (old_cleanups);
}
+ if (record_get_sig)
+ status->value.sig = TARGET_SIGNAL_INT;
+
+ do_cleanups (signal_cleanups);
do_cleanups (set_cleanups);
- return inferior_ptid;
+ return ret_ptid;
}
/* "to_disconnect" method for process record target. */
@@ -1311,6 +1676,12 @@ record_registers_change (struct regcache
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
+ if (!ptid_equal (record_prev_ptid, inferior_ptid))
+ {
+ record_arch_list_add_ptid (&record_prev_ptid);
+ record_prev_ptid = inferior_ptid;
+ }
+
if (regnum < 0)
{
int i;
@@ -1433,6 +1804,11 @@ record_xfer_partial (struct target_ops *
/* Record registers change to list as an instruction. */
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
+ if (!ptid_equal (record_prev_ptid, inferior_ptid))
+ {
+ record_arch_list_add_ptid (&record_prev_ptid);
+ record_prev_ptid = inferior_ptid;
+ }
if (record_arch_list_add_mem (offset, len))
{
record_list_release (record_arch_list_tail);
@@ -2240,7 +2616,7 @@ cmd_record_save (char *args, int from_tt
if (record_list == &record_first)
break;
- record_exec_insn (regcache, gdbarch, record_list);
+ record_exec_insn (®cache, &gdbarch, record_list);
if (record_list->prev)
record_list = record_list->prev;
@@ -2367,7 +2743,7 @@ cmd_record_save (char *args, int from_tt
}
/* Execute entry. */
- record_exec_insn (regcache, gdbarch, record_list);
+ record_exec_insn (®cache, &gdbarch, record_list);
if (record_list->next)
record_list = record_list->next;
@@ -2382,7 +2758,7 @@ cmd_record_save (char *args, int from_tt
if (record_list == cur_record_list)
break;
- record_exec_insn (regcache, gdbarch, record_list);
+ record_exec_insn (®cache, &gdbarch, record_list);
if (record_list->prev)
record_list = record_list->prev;
--- a/record.h
+++ b/record.h
@@ -23,6 +23,7 @@
#define RECORD_IS_USED (current_target.to_stratum == record_stratum)
extern int record_debug;
+extern int record_step;
extern int record_arch_list_add_reg (struct regcache *regcache, int num);
extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [RFC] Prec multi-thread support 2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu @ 2009-11-02 18:14 ` Michael Snyder 2009-11-09 18:00 ` Tom Tromey 1 sibling, 0 replies; 5+ messages in thread From: Michael Snyder @ 2009-11-02 18:14 UTC (permalink / raw) To: Hui Zhu; +Cc: gdb-patches ml Hui Zhu wrote: > Hi guys, > > This patches just for discussions and comments. Please help me with it. > > Thanks, > Hui Wow, great that you're working on this. Two ways to help get discussion started: 1) Tell us in plain text what you're doing (I know you know how hard it is to read someone else's code! <g>) 2) Start writing a test script to illustrate what works and what doesn't work yet. Michael ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] Prec multi-thread support 2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu 2009-11-02 18:14 ` Michael Snyder @ 2009-11-09 18:00 ` Tom Tromey 2009-11-12 8:26 ` Hui Zhu 1 sibling, 1 reply; 5+ messages in thread From: Tom Tromey @ 2009-11-09 18:00 UTC (permalink / raw) To: Hui Zhu; +Cc: gdb-patches ml, Michael Snyder >>>>> ">" == Hui Zhu <teawater@gmail.com> writes: >> This patches just for discussions and comments. Please help me with it. >> + struct record_message_args args; >> + >> + args.regcache = get_current_regcache (); >> + args.signal = signal; >> + record_message (&args); Directly calling record_message like this is ugly, because it avoids type-checking by the compiler. I would recommend restructuring this code in one of two possible ways. The first approach is to make record_message a simple trampoline that takes a void*, decodes it, and then calls a properly-typed function. Then, only use record_message in calls to catch_errors -- direct calls should call the properly-typed implementation function. The second approach is to avoid catch_errors altogether and use TRY_CATCH instead. This lets you avoid trampolines entirely. I prefer this way, but I don't insist on it. Tom ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] Prec multi-thread support 2009-11-09 18:00 ` Tom Tromey @ 2009-11-12 8:26 ` Hui Zhu 2009-11-13 9:27 ` Hui Zhu 0 siblings, 1 reply; 5+ messages in thread From: Hui Zhu @ 2009-11-12 8:26 UTC (permalink / raw) To: gdb-patches ml; +Cc: Michael Snyder, tromey [-- Attachment #1: Type: text/plain, Size: 280 bytes --] Hi guys, This is the new version for multi-thread support. It make multi-thread work OK with record save and support thread exit when record. I think the code work of prec multi-thread support is done. I can begin to do some make it clear and some other things. Thanks, Hui [-- Attachment #2: prec-x86-add-insn.txt --] [-- Type: text/plain, Size: 734 bytes --] --- i386-tdep.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) --- a/i386-tdep.c +++ b/i386-tdep.c @@ -4847,9 +4847,6 @@ reswitch: /* int3 */ /* XXX */ case 0xcc: - printf_unfiltered (_("Process record doesn't support instruction " - "int3.\n")); - ir.addr -= 1; goto no_support; break; @@ -4958,10 +4955,8 @@ reswitch: /* rdtsc */ case 0x0f31: - printf_unfiltered (_("Process record doesn't support " - "instruction rdtsc.\n")); - ir.addr -= 2; - goto no_support; + I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM); + I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM); break; /* sysenter */ [-- Attachment #3: prec-thread.txt --] [-- Type: text/plain, Size: 39182 bytes --] --- amd64-linux-tdep.c | 4 gdbthread.h | 11 i386-linux-tdep.c | 4 i386-tdep.c | 52 ++-- i386-tdep.h | 6 linux-record.c | 13 - linux-record.h | 2 record.c | 675 +++++++++++++++++++++++++++++++++++++++++++++-------- record.h | 1 thread.c | 9 10 files changed, 641 insertions(+), 136 deletions(-) --- a/amd64-linux-tdep.c +++ b/amd64-linux-tdep.c @@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li #define RECORD_ARCH_GET_GS 0x1004 static int -amd64_linux_syscall_record (struct regcache *regcache) +amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr) { int ret; ULONGEST syscall_native; @@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca } else { - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &amd64_linux_record_tdep); if (ret) return ret; --- a/gdbthread.h +++ b/gdbthread.h @@ -29,6 +29,15 @@ struct symtab; #include "ui-out.h" #include "inferior.h" +/* Frontend view of the thread state. Possible extensions: stepping, + finishing, until(ling),... */ +enum thread_state +{ + THREAD_STOPPED, + THREAD_RUNNING, + THREAD_EXITED, +}; + struct thread_info { struct thread_info *next; @@ -37,6 +46,8 @@ struct thread_info kernel thread id, etc. */ int num; /* Convenient handle (GDB thread id) */ + int record_is_waiting; + /* Non-zero means the thread is executing. Note: this is different from saying that there is an active target and we are stopped at a breakpoint, for instance. This is a real indicator whether the --- a/i386-linux-tdep.c +++ b/i386-linux-tdep.c @@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall) static struct linux_record_tdep i386_linux_record_tdep; static int -i386_linux_intx80_sysenter_record (struct regcache *regcache) +i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr) { int ret; LONGEST syscall_native; @@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc return 0; } - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &i386_linux_record_tdep); if (ret) return ret; --- a/i386-tdep.c +++ b/i386-tdep.c @@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor if (irp->override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because it " - "can't get the value of the segment register."), - paddress (gdbarch, irp->orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because it " + "can't get the value of the segment register."), + paddress (gdbarch, irp->orig_addr)); return 0; } @@ -4042,11 +4043,12 @@ reswitch: case 0xa3: if (ir.override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because " - "it can't get the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because " + "it can't get the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4467,11 +4469,12 @@ reswitch: if (ir.aflag && (es != ds)) { /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */ - warning (_("Process record ignores the memory " - "change of instruction at address %s " - "because it can't get the value of the " - "ES segment register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at address %s " + "because it can't get the value of the " + "ES segment register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4873,7 +4876,7 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr); if (ret) return ret; } @@ -4975,7 +4978,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5000,7 +5004,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5136,12 +5141,13 @@ reswitch: /* sidt */ if (ir.override >= 0) { - warning (_("Process record ignores the memory " - "change of instruction at " - "address %s because it can't get " - "the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at " + "address %s because it can't get " + "the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { --- a/i386-tdep.h +++ b/i386-tdep.h @@ -115,11 +115,11 @@ struct gdbarch_tdep in GDB is not same as I386 instructions. */ const int *record_regmap; /* Parse intx80 args. */ - int (*i386_intx80_record) (struct regcache *regcache); + int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse sysenter args. */ - int (*i386_sysenter_record) (struct regcache *regcache); + int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse syscall args. */ - int (*i386_syscall_record) (struct regcache *regcache); + int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr); }; /* Floating-point registers. */ --- a/linux-record.c +++ b/linux-record.c @@ -21,6 +21,7 @@ #include "target.h" #include "gdbtypes.h" #include "regcache.h" +#include "inferior.h" #include "record.h" #include "linux-record.h" @@ -222,7 +223,7 @@ record_linux_msghdr (struct regcache *re Return -1 if something wrong. */ int -record_linux_system_call (enum gdb_syscall syscall, +record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep) { @@ -242,8 +243,9 @@ record_linux_system_call (enum gdb_sysca int q; target_terminal_ours (); q = yquery (_("The next instruction is syscall exit. " - "It will make the program exit. " - "Do you want to stop the program?")); + "It will make the thread %s exit. " + "Do you want to stop the program?"), + target_pid_to_str (inferior_ptid)); target_terminal_inferior (); if (q) return 1; @@ -1209,10 +1211,13 @@ record_linux_system_call (enum gdb_sysca case gdb_sys_fsync: case gdb_sys_sigreturn: - case gdb_sys_clone: case gdb_sys_setdomainname: break; + case gdb_sys_clone: + record_step = 0; + break; + case gdb_sys_newuname: regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest); if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest, --- a/linux-record.h +++ b/linux-record.h @@ -534,7 +534,7 @@ enum gdb_syscall { /* Record a linux syscall. */ -extern int record_linux_system_call (enum gdb_syscall num, +extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep); #endif /* _LINUX_RECORD_H_ */ --- a/record.c +++ b/record.c @@ -30,6 +30,7 @@ #include "record.h" #include "elf-bfd.h" #include "gcore.h" +#include "observer.h" #include <signal.h> @@ -60,6 +61,8 @@ #define RECORD_FILE_MAGIC netorder32(0x20091016) +#define RECORD_THREAD (record_ops.beneath->to_stratum == thread_stratum) + /* These are the core structs of the process record functionality. A record_entry is a record of the value change of a register @@ -106,7 +109,8 @@ enum record_type { record_end = 0, record_reg, - record_mem + record_mem, + record_ptid }; /* This is the data structure that makes up the execution log. @@ -144,6 +148,8 @@ struct record_entry struct record_reg_entry reg; /* mem */ struct record_mem_entry mem; + /* ptid */ + ptid_t ptid; /* end */ struct record_end_entry end; } u; @@ -196,10 +202,19 @@ static int record_insn_num = 0; than count of insns presently in execution log). */ static ULONGEST record_insn_count; +/* In record mode, it's the step of next resume. */ +int record_step; + +/* The current inferior_ptid that is recorded. */ +static ptid_t record_prev_ptid; + /* The target_ops of process record. */ static struct target_ops record_ops; static struct target_ops record_core_ops; +static struct observer *record_new_thread_observer = NULL; +static struct observer *record_thread_exit_observer = NULL; + /* The beneath function pointers. */ static struct target_ops *record_beneath_to_resume_ops; static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int, @@ -285,6 +300,27 @@ record_mem_release (struct record_entry xfree (rec); } +/* Alloc a record_ptid record entry. */ + +static inline struct record_entry * +record_ptid_alloc (void) +{ + struct record_entry *rec; + + rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); + rec->type = record_ptid; + + return rec; +} + +/* Free a record_ptid record entry. */ + +static inline void +record_ptid_release (struct record_entry *rec) +{ + xfree (rec); +} + /* Alloc a record_end record entry. */ static inline struct record_entry * @@ -321,6 +357,9 @@ record_entry_release (struct record_entr case record_mem: record_mem_release (rec); break; + case record_ptid: + record_ptid_release (rec); + break; case record_end: record_end_release (rec); break; @@ -328,6 +367,16 @@ record_entry_release (struct record_entr return type; } +/* Remove one record entry from record_list. */ + +static void +record_entry_remove_from_list (struct record_entry *rec) +{ + if (rec->next) + rec->next->prev = rec->prev; + rec->prev->next = rec->next; +} + /* Free all record entries in list pointed to by REC. */ static void @@ -509,6 +558,24 @@ record_arch_list_add_mem (CORE_ADDR addr return 0; } +static void +record_arch_list_add_ptid (ptid_t *ptid) +{ + struct record_entry *rec; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: add ptid = %s to " + "record list.\n", + target_pid_to_str (*ptid)); + + rec = record_ptid_alloc (); + + rec->u.ptid = *ptid; + + record_arch_list_add (rec); +} + /* Add a record_end type struct record_entry to record_arch_list. */ int @@ -570,7 +637,6 @@ record_arch_list_cleanups (void *ignore) record_arch_list, and add it to record_list. */ struct record_message_args { - struct regcache *regcache; enum target_signal signal; }; @@ -579,15 +645,21 @@ record_message (void *args) { int ret; struct record_message_args *myargs = args; - struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache); + struct regcache *regcache; + struct gdbarch *gdbarch; struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0); + record_step = 1; + record_arch_list_head = NULL; record_arch_list_tail = NULL; /* Check record_insn_num. */ record_check_insn_num (1); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + /* If gdb sends a signal value to target_resume, save it in the 'end' field of the previous instruction. @@ -610,20 +682,45 @@ record_message (void *args) But we should still deliver the signal to gdb during the replay, if we delivered it during the recording. Therefore we should record the signal during record_wait, not record_resume. */ - if (record_list != &record_first) /* FIXME better way to check */ + if (ptid_equal (record_prev_ptid, inferior_ptid)) { - gdb_assert (record_list->type == record_end); - record_list->u.end.sigval = myargs->signal; + if (myargs->signal != TARGET_SIGNAL_0) + { + gdb_assert (record_list->type == record_end); + record_list->u.end.sigval = myargs->signal; + } + } + else + { + if (myargs->signal != TARGET_SIGNAL_0) + { + struct record_entry *rec; + for (rec = record_list; rec != record_first.next; rec = rec->prev) + { + if (rec->type == record_ptid + && ptid_equal (rec->u.ptid, inferior_ptid)) + { + gdb_assert (rec->prev->type == record_end); + rec->prev->u.end.sigval = myargs->signal; + break; + } + } + } + + /* If the inferior_ptid change (inferior_ptid is not same with + record_prev_ptid), record record_prev_ptid to record list. */ + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; } if (myargs->signal == TARGET_SIGNAL_0 || !gdbarch_process_record_signal_p (gdbarch)) ret = gdbarch_process_record (gdbarch, - myargs->regcache, - regcache_read_pc (myargs->regcache)); + regcache, + regcache_read_pc (regcache)); else ret = gdbarch_process_record_signal (gdbarch, - myargs->regcache, + regcache, myargs->signal); if (ret > 0) @@ -646,12 +743,10 @@ record_message (void *args) } static int -do_record_message (struct regcache *regcache, - enum target_signal signal, int catch) +do_record_message (enum target_signal signal, int catch) { struct record_message_args args; - args.regcache = regcache; args.signal = signal; if (catch) @@ -682,9 +777,12 @@ record_gdb_operation_disable_set (void) entries and memory entries, followed by an 'end' entry. */ static inline void -record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, +record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p, struct record_entry *entry) { + struct regcache *regcache = *regcache_p; + struct gdbarch *gdbarch = *gdbarch_p; + switch (entry->type) { case record_reg: /* reg */ @@ -748,6 +846,103 @@ record_exec_insn (struct regcache *regca } } break; + + case record_ptid: /* ptid */ + { + ptid_t tmp_ptid; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_ptid %s to " + "inferior ptid = %s.\n", + host_address_to_string (entry), + target_pid_to_str (entry->u.ptid)); + + if (!ptid_equal (entry->u.ptid, null_ptid)) + { + inferior_ptid = entry->u.ptid; + *regcache_p = get_current_regcache (); + *gdbarch_p = get_regcache_arch (*regcache_p); + } + tmp_ptid = entry->u.ptid; + entry->u.ptid = record_prev_ptid; + record_prev_ptid = tmp_ptid; + } + break; + } +} + +static int record_thread_number; + +static int +record_thread_number_count (struct thread_info *thread, void *unused) +{ + if (thread->state_ != THREAD_EXITED) + record_thread_number ++; + + return 0; +} + +static void +record_thread_number_reset (void) +{ + record_thread_number = 0; + + if (RECORD_THREAD) + iterate_over_threads (record_thread_number_count, NULL); +} + +static void +record_new_thread_handler (struct thread_info *tp) +{ + tp->record_is_waiting = 0; + + record_thread_number_reset (); +} + +static void +record_thread_exit_handler (struct thread_info *tp, int silent) +{ + struct record_entry *rec; + ptid_t cur_ptid = record_prev_ptid; + + if (!record_first.next) + return; + + tp->record_is_waiting = 0; + + if (tp->state_ != THREAD_EXITED) + { + gdb_assert (record_thread_number > 0); + record_thread_number --; + } + + /* Delete all the record_reg and record_ptid for tp->ptid + from record list. */ + for (rec = record_list; rec != &record_first;) + { + struct record_entry *tmp = rec; + rec = rec->prev; + + switch (tmp->type) { + case record_reg: + /* If this record_reg is for tp->ptid, delte it. */ + if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + case record_ptid: + /* If this record_ptid is for tp->ptid, delte it. */ + cur_ptid = tmp->u.ptid; + if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + } } } @@ -842,6 +1037,9 @@ record_open_1 (char *name, int from_tty) error (_("Could not find 'to_remove_breakpoint' method on the target stack.")); push_target (&record_ops); + + record_new_thread_observer = observer_attach_new_thread (record_new_thread_handler); + record_thread_exit_observer = observer_attach_thread_exit (record_thread_exit_handler); } /* "to_open" target method. Open the process record target. */ @@ -907,6 +1105,7 @@ record_open (char *name, int from_tty) record_insn_count = 0; record_list = &record_first; record_list->next = NULL; + record_prev_ptid = null_ptid; /* Set the tmp beneath pointers to beneath pointers. */ record_beneath_to_resume_ops = tmp_to_resume_ops; @@ -924,6 +1123,8 @@ record_open (char *name, int from_tty) record_core_open_1 (name, from_tty); else record_open_1 (name, from_tty); + + record_thread_number_reset (); } /* "to_close" target method. Close the process record target. */ @@ -936,6 +1137,17 @@ record_close (int quitting) if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); + if (record_new_thread_observer) + { + observer_detach_new_thread (record_new_thread_observer); + record_new_thread_observer = NULL; + } + if (record_thread_exit_observer) + { + observer_detach_thread_exit (record_thread_exit_observer); + record_thread_exit_observer = NULL; + } + record_list_release (record_list); /* Release record_core_regbuf. */ @@ -958,6 +1170,8 @@ record_close (int quitting) } static int record_resume_step = 0; +static ptid_t record_resume_ptid; +static enum target_signal record_resume_signal = TARGET_SIGNAL_0; /* "to_resume" target method. Resume the process record target. */ @@ -969,12 +1183,189 @@ record_resume (struct target_ops *ops, p if (!RECORD_IS_REPLAY) { - do_record_message (get_current_regcache (), signal, 0); - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - signal); + record_resume_ptid = ptid; + if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid)) + { + record_resume_signal = signal; + } + else + { + do_record_message (signal, 0); + record_beneath_to_resume (record_beneath_to_resume_ops, ptid, + record_step, signal); + } } } +struct record_thread_wait_args { + ptid_t real_inferior_ptid; + struct thread_info *real_inferior_tp; + struct target_waitstatus real_inferior_status; + + struct target_waitstatus *status; + int options; +}; + +static int +record_thread_reset_callback (struct thread_info *tp, void *data) +{ + tp->record_is_waiting = 0; + + return 0; +} + +static int +record_thread_check_callback (struct thread_info *tp, void *data) +{ + if (tp->state_ != THREAD_EXITED && tp->record_is_waiting) + return 1; + + return 0; +} + +/* Return 1 if it is not a simple step. + Return 0 if it is a simple step. */ + +static int +record_thread_wait (ptid_t ptid, struct target_waitstatus *status, + int options, ptid_t *ret_ptid) +{ + ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); + + if (ret_ptid) + *ret_ptid = wait_ptid; + + if (status->kind == TARGET_WAITKIND_IGNORE) + return 0; + + inferior_ptid = wait_ptid; + + /* Is this a SIGTRAP? */ + if (status->kind == TARGET_WAITKIND_STOPPED + && status->value.sig == TARGET_SIGNAL_TRAP) + { + CORE_ADDR tmp_pc; + struct regcache *regcache; + struct gdbarch *gdbarch; + + /* Yes -- check if there is a breakpoint. */ + registers_changed (); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + tmp_pc = regcache_read_pc (regcache); + if (!record_step) + tmp_pc -= gdbarch_decr_pc_after_break (gdbarch); + if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc)) + { + /* There is a breakpoint. GDB will want to stop. */ + if (record_step) + { + CORE_ADDR decr_pc_after_break + = gdbarch_decr_pc_after_break (gdbarch); + if (decr_pc_after_break) + regcache_write_pc (regcache, tmp_pc + decr_pc_after_break); + } + + return 1; + } + + return 0; + } + + return 1; +} + +static int +record_thread_wait_callback (struct thread_info *tp, void *data) +{ + struct record_thread_wait_args *args = data; + enum target_signal sig = TARGET_SIGNAL_0; + int options = args->options; + int ret; + + if (tp->state_ == THREAD_EXITED) + return 0; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "resume %s\n", target_pid_to_str (tp->ptid)); + + inferior_ptid = tp->ptid; + + if (!tp->record_is_waiting) + { + if (record_resume_signal != TARGET_SIGNAL_0 + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + { + sig = record_resume_signal; + record_resume_signal = TARGET_SIGNAL_0; + } + if (!do_record_message (sig, 1)) + { + args->status->kind = TARGET_WAITKIND_STOPPED; + args->status->value.sig = TARGET_SIGNAL_0; + return 1; + } + + if (record_step == 0) + { + /* The next insn is a sys_clone. */ + if (iterate_over_threads (record_thread_check_callback, NULL)) + target_stop (minus_one_ptid); + non_stop = 0; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid, + record_step, sig); + } + else + { + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "not resume %s\n", target_pid_to_str (tp->ptid)); + } + + if (record_step) + options |= TARGET_WNOHANG; + ret = record_thread_wait (inferior_ptid, args->status, + options, NULL); + if (args->status->kind == TARGET_WAITKIND_IGNORE) + { + if (!tp->record_is_waiting) + { + tp->record_is_waiting = 1; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "start waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + } + else + { + if (tp->record_is_waiting) + { + tp->record_is_waiting = 0; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "stop waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + + if (!args->real_inferior_tp + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + args->real_inferior_tp = tp; + if (args->real_inferior_tp == tp) + args->real_inferior_status = *args->status; + } + + return ret; +} + static int record_get_sig = 0; /* SIGINT signal handler, registered by "to_wait" method. */ @@ -994,7 +1385,13 @@ record_sig_handler (int signo) } static void -record_wait_cleanups (void *ignore) +record_wait_signal_cleanups (void *ignore) +{ + signal (SIGINT, handle_sigint); +} + +static void +record_wait_replay_cleanups (void *ignore) { if (execution_direction == EXEC_REVERSE) { @@ -1005,6 +1402,16 @@ record_wait_cleanups (void *ignore) record_list = record_list->prev; } +static void +record_wait_mthread_cleanups (void *ignore) +{ + /* Stop the threads that still running. */ + if (iterate_over_threads (record_thread_check_callback, NULL)) + target_stop (minus_one_ptid); + + non_stop = 0; +} + /* "to_wait" target method for process record target. In record mode, the target is always run in singlestep mode @@ -1023,7 +1430,10 @@ record_wait (struct target_ops *ops, ptid_t ptid, struct target_waitstatus *status, int options) { + ptid_t ret_ptid; struct cleanup *set_cleanups = record_gdb_operation_disable_set (); + struct cleanup *signal_cleanups + = make_cleanup (record_wait_signal_cleanups, 0); if (record_debug) fprintf_unfiltered (gdb_stdlog, @@ -1031,81 +1441,103 @@ record_wait (struct target_ops *ops, "record_resume_step = %d\n", record_resume_step); + record_get_sig = 0; + signal (SIGINT, record_sig_handler); + if (!RECORD_IS_REPLAY && ops != &record_core_ops) { - if (record_resume_step) - { - /* This is a single step. */ - return record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); - } - else - { - /* This is not a single step. */ - ptid_t ret; - CORE_ADDR tmp_pc; - - while (1) - { - ret = record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); + /* Record mode. */ + if (record_thread_number > 1 && !ptid_equal (record_resume_ptid, + inferior_ptid)) + { + /* Multi-threads record. */ + struct record_thread_wait_args args; + struct thread_info *tp; + struct cleanup *mthread_cleanups + = make_cleanup (record_wait_mthread_cleanups, 0); - /* Is this a SIGTRAP? */ - if (status->kind == TARGET_WAITKIND_STOPPED - && status->value.sig == TARGET_SIGNAL_TRAP) - { - struct regcache *regcache; + args.real_inferior_ptid = inferior_ptid; + args.real_inferior_tp = NULL; + args.status = status; + args.options = options; + non_stop = 1; + iterate_over_threads (record_thread_reset_callback, NULL); - /* Yes -- check if there is a breakpoint. */ - registers_changed (); - regcache = get_current_regcache (); - tmp_pc = regcache_read_pc (regcache); - if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), - tmp_pc)) - { - /* There is a breakpoint. GDB will want to stop. */ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - CORE_ADDR decr_pc_after_break - = gdbarch_decr_pc_after_break (gdbarch); - if (decr_pc_after_break) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); - } - else - { - /* There is not a breakpoint, and gdb is not - stepping, therefore gdb will not stop. - Therefore we will not return to gdb. - Record the insn and resume. */ - if (!do_record_message (regcache, TARGET_SIGNAL_0, 1)) - { - status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_0; - break; - } + while (1) + { + tp = iterate_over_threads (record_thread_wait_callback, &args); + if (tp) + break; + if (record_resume_step + && args.real_inferior_tp + && !args.real_inferior_tp->record_is_waiting) + { + *status = args.real_inferior_status; + inferior_ptid = args.real_inferior_ptid; + break; + } + } - record_beneath_to_resume (record_beneath_to_resume_ops, - ptid, 1, - TARGET_SIGNAL_0); - continue; - } - } + do_cleanups (mthread_cleanups); - /* The inferior is broken by a breakpoint or a signal. */ - break; + ret_ptid = inferior_ptid; + } + else + { + /* Single-thread record. */ + if (record_resume_step) + { + /* This is a single step. */ + ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); } + else + { + /* This is not a single step. */ + while (1) + { + if (record_thread_wait (ptid, status, + options, &ret_ptid)) + break; - return ret; - } + if (record_resume_step) + break; + + /* There is not a breakpoint, and gdb is not + stepping, therefore gdb will not stop. + Therefore we will not return to gdb. + Record the insn and resume. */ + if (!do_record_message (TARGET_SIGNAL_0, 1)) + { + ret_ptid = inferior_ptid; + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_0; + break; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, + record_resume_ptid, record_step, + TARGET_SIGNAL_0); + } + } + } } else { - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = get_regcache_arch (regcache); + /* Replay mode. */ + struct regcache *regcache; + struct gdbarch *gdbarch; int continue_flag = 1; int first_record_end = 1; - struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0); + struct cleanup *old_cleanups = + make_cleanup (record_wait_replay_cleanups, 0); CORE_ADDR tmp_pc; + ptid_t old_inferior_ptid = inferior_ptid; + + if (!ptid_equal (record_prev_ptid, null_ptid)) + inferior_ptid = record_prev_ptid; + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); status->kind = TARGET_WAITKIND_STOPPED; @@ -1129,8 +1561,6 @@ record_wait (struct target_ops *ops, } } - record_get_sig = 0; - signal (SIGINT, record_sig_handler); /* If GDB is in terminal_inferior mode, it will not get the signal. And in GDB replay mode, GDB doesn't need to be in terminal_inferior mode, because inferior will not executed. @@ -1161,7 +1591,7 @@ record_wait (struct target_ops *ops, break; } - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->type == record_end) { @@ -1184,12 +1614,13 @@ record_wait (struct target_ops *ops, In EXEC_FORWARD mode, this is the record_end of current instruction. */ /* step */ - if (record_resume_step) + if (record_resume_step + && ptid_equal (inferior_ptid, old_inferior_ptid)) { if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, "Process record: step.\n"); - continue_flag = 0; + continue_flag = 0; } /* check breakpoint */ @@ -1233,22 +1664,23 @@ record_wait (struct target_ops *ops, } while (continue_flag); - signal (SIGINT, handle_sigint); - replay_out: - if (record_get_sig) - status->value.sig = TARGET_SIGNAL_INT; - else if (record_list->u.end.sigval != TARGET_SIGNAL_0) + if (record_list->u.end.sigval != TARGET_SIGNAL_0) /* FIXME: better way to check */ status->value.sig = record_list->u.end.sigval; else status->value.sig = TARGET_SIGNAL_TRAP; + ret_ptid = inferior_ptid; discard_cleanups (old_cleanups); } + if (record_get_sig) + status->value.sig = TARGET_SIGNAL_INT; + + do_cleanups (signal_cleanups); do_cleanups (set_cleanups); - return inferior_ptid; + return ret_ptid; } /* "to_disconnect" method for process record target. */ @@ -1311,6 +1743,12 @@ record_registers_change (struct regcache record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } + if (regnum < 0) { int i; @@ -1433,6 +1871,11 @@ record_xfer_partial (struct target_ops * /* Record registers change to list as an instruction. */ record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } if (record_arch_list_add_mem (offset, len)) { record_list_release (record_arch_list_tail); @@ -2088,6 +2531,32 @@ record_restore (void) rec->u.mem.len); break; + case record_ptid: /* ptid */ + rec = record_ptid_alloc (); + + /* Get Process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.pid = (int) netorder64 (addr); + + /* Get Lightweight process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.lwp = (long) netorder64 (addr); + + /* Get Thread id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.tid = (long) netorder64 (addr); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Reading ptid %s (1 + 8 + 8 + 8 bytes), offset == %s\n", + target_pid_to_str (record_list->u.ptid), + paddress (get_current_arch (), + bfd_offset)); + break; + case record_end: /* end */ rec = record_end_alloc (); record_insn_num ++; @@ -2240,7 +2709,7 @@ cmd_record_save (char *args, int from_tt if (record_list == &record_first) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; @@ -2261,6 +2730,9 @@ cmd_record_save (char *args, int from_tt case record_mem: save_size += 1 + 4 + 8 + record_list->u.mem.len; break; + case record_ptid: + save_size += 1 + 8 + 8 + 8; + break; } /* Make the new bfd section. */ @@ -2347,6 +2819,25 @@ cmd_record_save (char *args, int from_tt record_list->u.mem.len, &bfd_offset); break; + case record_ptid: /* ptid */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Writing ptid %s (1 plus 8 plus 8 plus 8 bytes)\n", + target_pid_to_str (record_list->u.ptid)); + + /* Write Process id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.pid); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Lightweight process id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.lwp); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Thread id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.tid); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + break; + case record_end: if (record_debug) fprintf_unfiltered (gdb_stdlog, "\ @@ -2367,7 +2858,7 @@ cmd_record_save (char *args, int from_tt } /* Execute entry. */ - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->next) record_list = record_list->next; @@ -2382,7 +2873,7 @@ cmd_record_save (char *args, int from_tt if (record_list == cur_record_list) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; --- a/record.h +++ b/record.h @@ -23,6 +23,7 @@ #define RECORD_IS_USED (current_target.to_stratum == record_stratum) extern int record_debug; +extern int record_step; extern int record_arch_list_add_reg (struct regcache *regcache, int num); extern int record_arch_list_add_mem (CORE_ADDR addr, int len); --- a/thread.c +++ b/thread.c @@ -63,15 +63,6 @@ static void thread_apply_command (char * static void restore_current_thread (ptid_t); static void prune_threads (void); -/* Frontend view of the thread state. Possible extensions: stepping, - finishing, until(ling),... */ -enum thread_state -{ - THREAD_STOPPED, - THREAD_RUNNING, - THREAD_EXITED, -}; - struct thread_info* inferior_thread (void) { ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] Prec multi-thread support 2009-11-12 8:26 ` Hui Zhu @ 2009-11-13 9:27 ` Hui Zhu 0 siblings, 0 replies; 5+ messages in thread From: Hui Zhu @ 2009-11-13 9:27 UTC (permalink / raw) To: gdb-patches ml; +Cc: Michael Snyder, tromey [-- Attachment #1: Type: text/plain, Size: 445 bytes --] Hi guys, The new patch fix some small bugs. Thanks, Hui On Thu, Nov 12, 2009 at 16:25, Hui Zhu <teawater@gmail.com> wrote: > Hi guys, > > This is the new version for multi-thread support. It make > multi-thread work OK with record save and support thread exit when > record. > I think the code work of prec multi-thread support is done. I can > begin to do some make it clear and some other things. > > Thanks, > Hui > [-- Attachment #2: prec-thread.txt --] [-- Type: text/plain, Size: 39263 bytes --] --- amd64-linux-tdep.c | 4 gdbthread.h | 11 i386-linux-tdep.c | 4 i386-tdep.c | 52 ++-- i386-tdep.h | 6 linux-record.c | 13 - linux-record.h | 2 record.c | 680 +++++++++++++++++++++++++++++++++++++++++++++-------- record.h | 1 thread.c | 9 10 files changed, 647 insertions(+), 135 deletions(-) --- a/amd64-linux-tdep.c +++ b/amd64-linux-tdep.c @@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li #define RECORD_ARCH_GET_GS 0x1004 static int -amd64_linux_syscall_record (struct regcache *regcache) +amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr) { int ret; ULONGEST syscall_native; @@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca } else { - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &amd64_linux_record_tdep); if (ret) return ret; --- a/gdbthread.h +++ b/gdbthread.h @@ -29,6 +29,15 @@ struct symtab; #include "ui-out.h" #include "inferior.h" +/* Frontend view of the thread state. Possible extensions: stepping, + finishing, until(ling),... */ +enum thread_state +{ + THREAD_STOPPED, + THREAD_RUNNING, + THREAD_EXITED, +}; + struct thread_info { struct thread_info *next; @@ -37,6 +46,8 @@ struct thread_info kernel thread id, etc. */ int num; /* Convenient handle (GDB thread id) */ + int record_is_waiting; + /* Non-zero means the thread is executing. Note: this is different from saying that there is an active target and we are stopped at a breakpoint, for instance. This is a real indicator whether the --- a/i386-linux-tdep.c +++ b/i386-linux-tdep.c @@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall) static struct linux_record_tdep i386_linux_record_tdep; static int -i386_linux_intx80_sysenter_record (struct regcache *regcache) +i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr) { int ret; LONGEST syscall_native; @@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc return 0; } - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &i386_linux_record_tdep); if (ret) return ret; --- a/i386-tdep.c +++ b/i386-tdep.c @@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor if (irp->override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because it " - "can't get the value of the segment register."), - paddress (gdbarch, irp->orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because it " + "can't get the value of the segment register."), + paddress (gdbarch, irp->orig_addr)); return 0; } @@ -4042,11 +4043,12 @@ reswitch: case 0xa3: if (ir.override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because " - "it can't get the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because " + "it can't get the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4467,11 +4469,12 @@ reswitch: if (ir.aflag && (es != ds)) { /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */ - warning (_("Process record ignores the memory " - "change of instruction at address %s " - "because it can't get the value of the " - "ES segment register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at address %s " + "because it can't get the value of the " + "ES segment register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4873,7 +4876,7 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr); if (ret) return ret; } @@ -4975,7 +4978,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5000,7 +5004,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5136,12 +5141,13 @@ reswitch: /* sidt */ if (ir.override >= 0) { - warning (_("Process record ignores the memory " - "change of instruction at " - "address %s because it can't get " - "the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at " + "address %s because it can't get " + "the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { --- a/i386-tdep.h +++ b/i386-tdep.h @@ -115,11 +115,11 @@ struct gdbarch_tdep in GDB is not same as I386 instructions. */ const int *record_regmap; /* Parse intx80 args. */ - int (*i386_intx80_record) (struct regcache *regcache); + int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse sysenter args. */ - int (*i386_sysenter_record) (struct regcache *regcache); + int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse syscall args. */ - int (*i386_syscall_record) (struct regcache *regcache); + int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr); }; /* Floating-point registers. */ --- a/linux-record.c +++ b/linux-record.c @@ -21,6 +21,7 @@ #include "target.h" #include "gdbtypes.h" #include "regcache.h" +#include "inferior.h" #include "record.h" #include "linux-record.h" @@ -222,7 +223,7 @@ record_linux_msghdr (struct regcache *re Return -1 if something wrong. */ int -record_linux_system_call (enum gdb_syscall syscall, +record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep) { @@ -242,8 +243,9 @@ record_linux_system_call (enum gdb_sysca int q; target_terminal_ours (); q = yquery (_("The next instruction is syscall exit. " - "It will make the program exit. " - "Do you want to stop the program?")); + "It will make the thread %s exit. " + "Do you want to stop the program?"), + target_pid_to_str (inferior_ptid)); target_terminal_inferior (); if (q) return 1; @@ -1209,10 +1211,13 @@ record_linux_system_call (enum gdb_sysca case gdb_sys_fsync: case gdb_sys_sigreturn: - case gdb_sys_clone: case gdb_sys_setdomainname: break; + case gdb_sys_clone: + record_step = 0; + break; + case gdb_sys_newuname: regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest); if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest, --- a/linux-record.h +++ b/linux-record.h @@ -534,7 +534,7 @@ enum gdb_syscall { /* Record a linux syscall. */ -extern int record_linux_system_call (enum gdb_syscall num, +extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep); #endif /* _LINUX_RECORD_H_ */ --- a/record.c +++ b/record.c @@ -30,6 +30,7 @@ #include "record.h" #include "elf-bfd.h" #include "gcore.h" +#include "observer.h" #include <signal.h> @@ -60,6 +61,8 @@ #define RECORD_FILE_MAGIC netorder32(0x20091016) +#define RECORD_THREAD (record_ops.beneath->to_stratum == thread_stratum) + /* These are the core structs of the process record functionality. A record_entry is a record of the value change of a register @@ -106,7 +109,8 @@ enum record_type { record_end = 0, record_reg, - record_mem + record_mem, + record_ptid }; /* This is the data structure that makes up the execution log. @@ -144,6 +148,8 @@ struct record_entry struct record_reg_entry reg; /* mem */ struct record_mem_entry mem; + /* ptid */ + ptid_t ptid; /* end */ struct record_end_entry end; } u; @@ -196,10 +202,19 @@ static int record_insn_num = 0; than count of insns presently in execution log). */ static ULONGEST record_insn_count; +/* In record mode, it's the step of next resume. */ +int record_step; + +/* The current inferior_ptid that is recorded. */ +static ptid_t record_prev_ptid; + /* The target_ops of process record. */ static struct target_ops record_ops; static struct target_ops record_core_ops; +static struct observer *record_new_thread_observer = NULL; +static struct observer *record_thread_exit_observer = NULL; + /* The beneath function pointers. */ static struct target_ops *record_beneath_to_resume_ops; static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int, @@ -285,6 +300,27 @@ record_mem_release (struct record_entry xfree (rec); } +/* Alloc a record_ptid record entry. */ + +static inline struct record_entry * +record_ptid_alloc (void) +{ + struct record_entry *rec; + + rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); + rec->type = record_ptid; + + return rec; +} + +/* Free a record_ptid record entry. */ + +static inline void +record_ptid_release (struct record_entry *rec) +{ + xfree (rec); +} + /* Alloc a record_end record entry. */ static inline struct record_entry * @@ -321,6 +357,9 @@ record_entry_release (struct record_entr case record_mem: record_mem_release (rec); break; + case record_ptid: + record_ptid_release (rec); + break; case record_end: record_end_release (rec); break; @@ -328,6 +367,16 @@ record_entry_release (struct record_entr return type; } +/* Remove one record entry from record_list. */ + +static void +record_entry_remove_from_list (struct record_entry *rec) +{ + if (rec->next) + rec->next->prev = rec->prev; + rec->prev->next = rec->next; +} + /* Free all record entries in list pointed to by REC. */ static void @@ -509,6 +558,24 @@ record_arch_list_add_mem (CORE_ADDR addr return 0; } +static void +record_arch_list_add_ptid (ptid_t *ptid) +{ + struct record_entry *rec; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: add ptid = %s to " + "record list.\n", + target_pid_to_str (*ptid)); + + rec = record_ptid_alloc (); + + rec->u.ptid = *ptid; + + record_arch_list_add (rec); +} + /* Add a record_end type struct record_entry to record_arch_list. */ int @@ -570,7 +637,6 @@ record_arch_list_cleanups (void *ignore) record_arch_list, and add it to record_list. */ struct record_message_args { - struct regcache *regcache; enum target_signal signal; }; @@ -579,15 +645,21 @@ record_message (void *args) { int ret; struct record_message_args *myargs = args; - struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache); + struct regcache *regcache; + struct gdbarch *gdbarch; struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0); + record_step = 1; + record_arch_list_head = NULL; record_arch_list_tail = NULL; /* Check record_insn_num. */ record_check_insn_num (1); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + /* If gdb sends a signal value to target_resume, save it in the 'end' field of the previous instruction. @@ -610,20 +682,45 @@ record_message (void *args) But we should still deliver the signal to gdb during the replay, if we delivered it during the recording. Therefore we should record the signal during record_wait, not record_resume. */ - if (record_list != &record_first) /* FIXME better way to check */ + if (ptid_equal (record_prev_ptid, inferior_ptid)) { - gdb_assert (record_list->type == record_end); - record_list->u.end.sigval = myargs->signal; + if (myargs->signal != TARGET_SIGNAL_0) + { + gdb_assert (record_list->type == record_end); + record_list->u.end.sigval = myargs->signal; + } + } + else + { + if (myargs->signal != TARGET_SIGNAL_0) + { + struct record_entry *rec; + for (rec = record_list; rec != record_first.next; rec = rec->prev) + { + if (rec->type == record_ptid + && ptid_equal (rec->u.ptid, inferior_ptid)) + { + gdb_assert (rec->prev->type == record_end); + rec->prev->u.end.sigval = myargs->signal; + break; + } + } + } + + /* If the inferior_ptid change (inferior_ptid is not same with + record_prev_ptid), record record_prev_ptid to record list. */ + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; } if (myargs->signal == TARGET_SIGNAL_0 || !gdbarch_process_record_signal_p (gdbarch)) ret = gdbarch_process_record (gdbarch, - myargs->regcache, - regcache_read_pc (myargs->regcache)); + regcache, + regcache_read_pc (regcache)); else ret = gdbarch_process_record_signal (gdbarch, - myargs->regcache, + regcache, myargs->signal); if (ret > 0) @@ -646,12 +743,10 @@ record_message (void *args) } static int -do_record_message (struct regcache *regcache, - enum target_signal signal, int catch) +do_record_message (enum target_signal signal, int catch) { struct record_message_args args; - args.regcache = regcache; args.signal = signal; if (catch) @@ -682,9 +777,12 @@ record_gdb_operation_disable_set (void) entries and memory entries, followed by an 'end' entry. */ static inline void -record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, +record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p, struct record_entry *entry) { + struct regcache *regcache = *regcache_p; + struct gdbarch *gdbarch = *gdbarch_p; + switch (entry->type) { case record_reg: /* reg */ @@ -748,6 +846,103 @@ record_exec_insn (struct regcache *regca } } break; + + case record_ptid: /* ptid */ + { + ptid_t tmp_ptid; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_ptid %s to " + "inferior ptid = %s.\n", + host_address_to_string (entry), + target_pid_to_str (entry->u.ptid)); + + if (!ptid_equal (entry->u.ptid, null_ptid)) + { + inferior_ptid = entry->u.ptid; + *regcache_p = get_current_regcache (); + *gdbarch_p = get_regcache_arch (*regcache_p); + } + tmp_ptid = entry->u.ptid; + entry->u.ptid = record_prev_ptid; + record_prev_ptid = tmp_ptid; + } + break; + } +} + +static int record_thread_number; + +static int +record_thread_number_count (struct thread_info *thread, void *unused) +{ + if (thread->state_ != THREAD_EXITED) + record_thread_number ++; + + return 0; +} + +static void +record_thread_number_reset (void) +{ + record_thread_number = 0; + + if (RECORD_THREAD) + iterate_over_threads (record_thread_number_count, NULL); +} + +static void +record_new_thread_handler (struct thread_info *tp) +{ + tp->record_is_waiting = 0; + + record_thread_number_reset (); +} + +static void +record_thread_exit_handler (struct thread_info *tp, int silent) +{ + struct record_entry *rec; + ptid_t cur_ptid = record_prev_ptid; + + if (!record_first.next) + return; + + tp->record_is_waiting = 0; + + if (tp->state_ != THREAD_EXITED) + { + gdb_assert (record_thread_number > 0); + record_thread_number --; + } + + /* Delete all the record_reg and record_ptid for tp->ptid + from record list. */ + for (rec = record_list; rec != &record_first;) + { + struct record_entry *tmp = rec; + rec = rec->prev; + + switch (tmp->type) { + case record_reg: + /* If this record_reg is for tp->ptid, delte it. */ + if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + case record_ptid: + /* If this record_ptid is for tp->ptid, delte it. */ + cur_ptid = tmp->u.ptid; + if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + } } } @@ -842,6 +1037,9 @@ record_open_1 (char *name, int from_tty) error (_("Could not find 'to_remove_breakpoint' method on the target stack.")); push_target (&record_ops); + + record_new_thread_observer = observer_attach_new_thread (record_new_thread_handler); + record_thread_exit_observer = observer_attach_thread_exit (record_thread_exit_handler); } /* "to_open" target method. Open the process record target. */ @@ -907,6 +1105,7 @@ record_open (char *name, int from_tty) record_insn_count = 0; record_list = &record_first; record_list->next = NULL; + record_prev_ptid = null_ptid; /* Set the tmp beneath pointers to beneath pointers. */ record_beneath_to_resume_ops = tmp_to_resume_ops; @@ -924,6 +1123,8 @@ record_open (char *name, int from_tty) record_core_open_1 (name, from_tty); else record_open_1 (name, from_tty); + + record_thread_number_reset (); } /* "to_close" target method. Close the process record target. */ @@ -936,6 +1137,17 @@ record_close (int quitting) if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); + if (record_new_thread_observer) + { + observer_detach_new_thread (record_new_thread_observer); + record_new_thread_observer = NULL; + } + if (record_thread_exit_observer) + { + observer_detach_thread_exit (record_thread_exit_observer); + record_thread_exit_observer = NULL; + } + record_list_release (record_list); /* Release record_core_regbuf. */ @@ -958,6 +1170,8 @@ record_close (int quitting) } static int record_resume_step = 0; +static ptid_t record_resume_ptid; +static enum target_signal record_resume_signal = TARGET_SIGNAL_0; /* "to_resume" target method. Resume the process record target. */ @@ -969,10 +1183,186 @@ record_resume (struct target_ops *ops, p if (!RECORD_IS_REPLAY) { - do_record_message (get_current_regcache (), signal, 0); - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - signal); + record_resume_ptid = ptid; + if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid)) + { + record_resume_signal = signal; + } + else + { + do_record_message (signal, 0); + record_beneath_to_resume (record_beneath_to_resume_ops, ptid, + record_step, signal); + } + } +} + +struct record_thread_wait_args { + ptid_t real_inferior_ptid; + struct thread_info *real_inferior_tp; + struct target_waitstatus real_inferior_status; + + struct target_waitstatus *status; + int options; +}; + +static int +record_thread_reset_callback (struct thread_info *tp, void *data) +{ + tp->record_is_waiting = 0; + + return 0; +} + +static int +record_thread_stop_callback (struct thread_info *tp, void *data) +{ + if (tp->state_ != THREAD_EXITED && tp->record_is_waiting) + target_stop (tp->ptid); + + return 0; +} + +/* Return 1 if it is not a simple step. + Return 0 if it is a simple step. */ + +static int +record_thread_wait (ptid_t ptid, struct target_waitstatus *status, + int options, ptid_t *ret_ptid) +{ + ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); + + if (ret_ptid) + *ret_ptid = wait_ptid; + + if (status->kind == TARGET_WAITKIND_IGNORE) + return 0; + + inferior_ptid = wait_ptid; + + /* Is this a SIGTRAP? */ + if (status->kind == TARGET_WAITKIND_STOPPED + && status->value.sig == TARGET_SIGNAL_TRAP) + { + CORE_ADDR tmp_pc; + struct regcache *regcache; + struct gdbarch *gdbarch; + + /* Yes -- check if there is a breakpoint. */ + registers_changed (); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + tmp_pc = regcache_read_pc (regcache); + if (!record_step) + tmp_pc -= gdbarch_decr_pc_after_break (gdbarch); + if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc)) + { + /* There is a breakpoint. GDB will want to stop. */ + if (record_step) + { + CORE_ADDR decr_pc_after_break + = gdbarch_decr_pc_after_break (gdbarch); + if (decr_pc_after_break) + regcache_write_pc (regcache, tmp_pc + decr_pc_after_break); + } + + return 1; + } + + return 0; + } + + return 1; +} + +static int +record_thread_wait_callback (struct thread_info *tp, void *data) +{ + struct record_thread_wait_args *args = data; + enum target_signal sig = TARGET_SIGNAL_0; + int options = args->options; + int ret; + + if (tp->state_ == THREAD_EXITED) + return 0; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "resume %s\n", target_pid_to_str (tp->ptid)); + + inferior_ptid = tp->ptid; + + if (!tp->record_is_waiting) + { + if (record_resume_signal != TARGET_SIGNAL_0 + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + { + sig = record_resume_signal; + record_resume_signal = TARGET_SIGNAL_0; + } + if (!do_record_message (sig, 1)) + { + args->status->kind = TARGET_WAITKIND_STOPPED; + args->status->value.sig = TARGET_SIGNAL_0; + return 1; + } + + if (record_step == 0) + { + /* The next insn is a sys_clone. */ + iterate_over_threads (record_thread_stop_callback, NULL); + non_stop = 0; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid, + record_step, sig); } + else + { + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "not resume %s\n", target_pid_to_str (tp->ptid)); + } + + if (record_step) + options |= TARGET_WNOHANG; + ret = record_thread_wait (inferior_ptid, args->status, + options, NULL); + if (args->status->kind == TARGET_WAITKIND_IGNORE) + { + if (!tp->record_is_waiting) + { + tp->record_is_waiting = 1; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "start waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + } + else + { + if (tp->record_is_waiting) + { + tp->record_is_waiting = 0; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "stop waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + + if (!args->real_inferior_tp + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + args->real_inferior_tp = tp; + if (args->real_inferior_tp == tp) + args->real_inferior_status = *args->status; + } + + return ret; } static int record_get_sig = 0; @@ -994,7 +1384,13 @@ record_sig_handler (int signo) } static void -record_wait_cleanups (void *ignore) +record_wait_signal_cleanups (void *ignore) +{ + signal (SIGINT, handle_sigint); +} + +static void +record_wait_replay_cleanups (void *ignore) { if (execution_direction == EXEC_REVERSE) { @@ -1005,6 +1401,17 @@ record_wait_cleanups (void *ignore) record_list = record_list->prev; } +static void +record_wait_mthread_cleanups (void *ignore) +{ + non_stop = 1; + + /* Stop the threads that still running. */ + iterate_over_threads (record_thread_stop_callback, NULL); + + non_stop = 0; +} + /* "to_wait" target method for process record target. In record mode, the target is always run in singlestep mode @@ -1023,7 +1430,10 @@ record_wait (struct target_ops *ops, ptid_t ptid, struct target_waitstatus *status, int options) { + ptid_t ret_ptid; struct cleanup *set_cleanups = record_gdb_operation_disable_set (); + struct cleanup *signal_cleanups + = make_cleanup (record_wait_signal_cleanups, 0); if (record_debug) fprintf_unfiltered (gdb_stdlog, @@ -1031,81 +1441,110 @@ record_wait (struct target_ops *ops, "record_resume_step = %d\n", record_resume_step); + record_get_sig = 0; + signal (SIGINT, record_sig_handler); + if (!RECORD_IS_REPLAY && ops != &record_core_ops) { - if (record_resume_step) - { - /* This is a single step. */ - return record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); - } - else - { - /* This is not a single step. */ - ptid_t ret; - CORE_ADDR tmp_pc; + /* Record mode. */ + if (record_thread_number > 1 && !ptid_equal (record_resume_ptid, + inferior_ptid)) + { + /* Multi-threads record. */ + struct record_thread_wait_args args; + struct thread_info *tp; + struct cleanup *mthread_cleanups + = make_cleanup (record_wait_mthread_cleanups, 0); - while (1) - { - ret = record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); + args.real_inferior_ptid = inferior_ptid; + args.real_inferior_tp = NULL; + args.status = status; + args.options = options; + non_stop = 1; + iterate_over_threads (record_thread_reset_callback, NULL); - /* Is this a SIGTRAP? */ - if (status->kind == TARGET_WAITKIND_STOPPED - && status->value.sig == TARGET_SIGNAL_TRAP) - { - struct regcache *regcache; + while (1) + { + tp = iterate_over_threads (record_thread_wait_callback, &args); + if (tp) + break; + if (record_resume_step + && args.real_inferior_tp + && !args.real_inferior_tp->record_is_waiting) + { + *status = args.real_inferior_status; + inferior_ptid = args.real_inferior_ptid; + break; + } + } - /* Yes -- check if there is a breakpoint. */ - registers_changed (); - regcache = get_current_regcache (); - tmp_pc = regcache_read_pc (regcache); - if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), - tmp_pc)) - { - /* There is a breakpoint. GDB will want to stop. */ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - CORE_ADDR decr_pc_after_break - = gdbarch_decr_pc_after_break (gdbarch); - if (decr_pc_after_break) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); - } - else - { - /* There is not a breakpoint, and gdb is not - stepping, therefore gdb will not stop. - Therefore we will not return to gdb. - Record the insn and resume. */ - if (!do_record_message (regcache, TARGET_SIGNAL_0, 1)) - { - status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_0; - break; - } + do_cleanups (mthread_cleanups); - record_beneath_to_resume (record_beneath_to_resume_ops, - ptid, 1, - TARGET_SIGNAL_0); - continue; - } - } + if (tp && tp->state_ != THREAD_EXITED) + { + inferior_ptid = tp->ptid; + } + ret_ptid = inferior_ptid; + } + else + { + /* Single-thread record. */ + if (record_thread_number > 1) + ptid = record_resume_ptid; - /* The inferior is broken by a breakpoint or a signal. */ - break; + if (record_resume_step) + { + /* This is a single step. */ + ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); } + else + { + /* This is not a single step. */ + while (1) + { + if (record_thread_wait (ptid, status, + options, &ret_ptid)) + break; - return ret; - } + if (record_resume_step) + break; + + /* There is not a breakpoint, and gdb is not + stepping, therefore gdb will not stop. + Therefore we will not return to gdb. + Record the insn and resume. */ + if (!do_record_message (TARGET_SIGNAL_0, 1)) + { + ret_ptid = inferior_ptid; + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_0; + break; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, + record_resume_ptid, record_step, + TARGET_SIGNAL_0); + } + } + } } else { - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = get_regcache_arch (regcache); + /* Replay mode. */ + struct regcache *regcache; + struct gdbarch *gdbarch; int continue_flag = 1; int first_record_end = 1; - struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0); + struct cleanup *old_cleanups = + make_cleanup (record_wait_replay_cleanups, 0); CORE_ADDR tmp_pc; + ptid_t old_inferior_ptid = inferior_ptid; + + if (!ptid_equal (record_prev_ptid, null_ptid)) + inferior_ptid = record_prev_ptid; + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); status->kind = TARGET_WAITKIND_STOPPED; @@ -1129,8 +1568,6 @@ record_wait (struct target_ops *ops, } } - record_get_sig = 0; - signal (SIGINT, record_sig_handler); /* If GDB is in terminal_inferior mode, it will not get the signal. And in GDB replay mode, GDB doesn't need to be in terminal_inferior mode, because inferior will not executed. @@ -1161,7 +1598,7 @@ record_wait (struct target_ops *ops, break; } - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->type == record_end) { @@ -1184,12 +1621,13 @@ record_wait (struct target_ops *ops, In EXEC_FORWARD mode, this is the record_end of current instruction. */ /* step */ - if (record_resume_step) + if (record_resume_step + && ptid_equal (inferior_ptid, old_inferior_ptid)) { if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, "Process record: step.\n"); - continue_flag = 0; + continue_flag = 0; } /* check breakpoint */ @@ -1233,22 +1671,23 @@ record_wait (struct target_ops *ops, } while (continue_flag); - signal (SIGINT, handle_sigint); - replay_out: - if (record_get_sig) - status->value.sig = TARGET_SIGNAL_INT; - else if (record_list->u.end.sigval != TARGET_SIGNAL_0) + if (record_list->u.end.sigval != TARGET_SIGNAL_0) /* FIXME: better way to check */ status->value.sig = record_list->u.end.sigval; else status->value.sig = TARGET_SIGNAL_TRAP; + ret_ptid = inferior_ptid; discard_cleanups (old_cleanups); } + if (record_get_sig) + status->value.sig = TARGET_SIGNAL_INT; + + do_cleanups (signal_cleanups); do_cleanups (set_cleanups); - return inferior_ptid; + return ret_ptid; } /* "to_disconnect" method for process record target. */ @@ -1311,6 +1750,12 @@ record_registers_change (struct regcache record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } + if (regnum < 0) { int i; @@ -1433,6 +1878,11 @@ record_xfer_partial (struct target_ops * /* Record registers change to list as an instruction. */ record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } if (record_arch_list_add_mem (offset, len)) { record_list_release (record_arch_list_tail); @@ -2088,6 +2538,32 @@ record_restore (void) rec->u.mem.len); break; + case record_ptid: /* ptid */ + rec = record_ptid_alloc (); + + /* Get Process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.pid = (int) netorder64 (addr); + + /* Get Lightweight process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.lwp = (long) netorder64 (addr); + + /* Get Thread id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.tid = (long) netorder64 (addr); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Reading ptid %s (1 + 8 + 8 + 8 bytes), offset == %s\n", + target_pid_to_str (record_list->u.ptid), + paddress (get_current_arch (), + bfd_offset)); + break; + case record_end: /* end */ rec = record_end_alloc (); record_insn_num ++; @@ -2240,7 +2716,7 @@ cmd_record_save (char *args, int from_tt if (record_list == &record_first) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; @@ -2261,6 +2737,9 @@ cmd_record_save (char *args, int from_tt case record_mem: save_size += 1 + 4 + 8 + record_list->u.mem.len; break; + case record_ptid: + save_size += 1 + 8 + 8 + 8; + break; } /* Make the new bfd section. */ @@ -2347,6 +2826,25 @@ cmd_record_save (char *args, int from_tt record_list->u.mem.len, &bfd_offset); break; + case record_ptid: /* ptid */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Writing ptid %s (1 plus 8 plus 8 plus 8 bytes)\n", + target_pid_to_str (record_list->u.ptid)); + + /* Write Process id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.pid); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Lightweight process id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.lwp); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Thread id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.tid); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + break; + case record_end: if (record_debug) fprintf_unfiltered (gdb_stdlog, "\ @@ -2367,7 +2865,7 @@ cmd_record_save (char *args, int from_tt } /* Execute entry. */ - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->next) record_list = record_list->next; @@ -2382,7 +2880,7 @@ cmd_record_save (char *args, int from_tt if (record_list == cur_record_list) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; --- a/record.h +++ b/record.h @@ -23,6 +23,7 @@ #define RECORD_IS_USED (current_target.to_stratum == record_stratum) extern int record_debug; +extern int record_step; extern int record_arch_list_add_reg (struct regcache *regcache, int num); extern int record_arch_list_add_mem (CORE_ADDR addr, int len); --- a/thread.c +++ b/thread.c @@ -63,15 +63,6 @@ static void thread_apply_command (char * static void restore_current_thread (ptid_t); static void prune_threads (void); -/* Frontend view of the thread state. Possible extensions: stepping, - finishing, until(ling),... */ -enum thread_state -{ - THREAD_STOPPED, - THREAD_RUNNING, - THREAD_EXITED, -}; - struct thread_info* inferior_thread (void) { [-- Attachment #3: prec-x86-add-insn.txt --] [-- Type: text/plain, Size: 734 bytes --] --- i386-tdep.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) --- a/i386-tdep.c +++ b/i386-tdep.c @@ -4847,9 +4847,6 @@ reswitch: /* int3 */ /* XXX */ case 0xcc: - printf_unfiltered (_("Process record doesn't support instruction " - "int3.\n")); - ir.addr -= 1; goto no_support; break; @@ -4958,10 +4955,8 @@ reswitch: /* rdtsc */ case 0x0f31: - printf_unfiltered (_("Process record doesn't support " - "instruction rdtsc.\n")); - ir.addr -= 2; - goto no_support; + I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REAX_REGNUM); + I386_RECORD_ARCH_LIST_ADD_REG (X86_RECORD_REDX_REGNUM); break; /* sysenter */ ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2009-11-13 9:27 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-11-02 16:45 [RFC] Prec multi-thread support Hui Zhu 2009-11-02 18:14 ` Michael Snyder 2009-11-09 18:00 ` Tom Tromey 2009-11-12 8:26 ` Hui Zhu 2009-11-13 9:27 ` Hui Zhu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox