From: Hui Zhu <teawater@gmail.com>
To: Michael Snyder <msnyder@vmware.com>, Eli Zaretskii <eliz@gnu.org>
Cc: gdb-patches ml <gdb-patches@sourceware.org>
Subject: Re: [RFA/RFC] Add dump and load command to process record and replay
Date: Mon, 03 Aug 2009 04:12:00 -0000 [thread overview]
Message-ID: <daef60380908022111t1023a92cnf0dc89ff7883a0b5@mail.gmail.com> (raw)
In-Reply-To: <daef60380908012258i1a719b27uc22ce1e5a4b2f1a9@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 32404 bytes --]
On Sun, Aug 2, 2009 at 13:58, Hui Zhu<teawater@gmail.com> wrote:
> OK. I will post new patches for them, memory and dump.
>
> Thanks,
> Hui
>
> On Sun, Aug 2, 2009 at 11:17, Michael Snyder<msnyder@vmware.com> wrote:
>> Michael Snyder wrote:
>>
>>> 3) I don't really understand how core files fit into this,
>>> but I'd love to discuss that idea in a separate patch thread.
>>
>> Oh, sorry -- I see how they're related now.
>> Very clever, actually.
>>
>> I'd still like to see them submitted separately, though,
>> because:
>> 1) the dump/restore part of the patch is cleaner and
>> closer to being acceptable, and can really be used on
>> its own, while
>> 2) the corefile part of the patch is kind of messy and
>> prototype-ish, and I think needs much more discussion and
>> cleaning up before it will be ready.
>>
>
Hi Eli and Michael,
I make a new patch according to your mail. It depend on patch in
http://sourceware.org/ml/gdb-patches/2009-08/msg00015.html
Please help me review it.
Thanks,
Hui
2009-08-03 Hui Zhu <teawater@gmail.com>
Add dump and load command to process record and replay.
* record.c (completer.h, arch-utils.h, gdbcore.h, exec.h,
byteswap.h, netinet/in.h): Include files.
(RECORD_IS_REPLAY): Return true if record_core is true.
(RECORD_FILE_MAGIC): New macro.
(record_core_buf_entry): New struct.
(record_core, record_core_regbuf, record_core_start,
record_core_end, record_core_buf_list,
record_beneath_to_fetch_registers_ops,
record_beneath_to_fetch_registers,
record_beneath_to_store_registers_ops,
record_beneath_to_store_registers,
record_beneath_to_has_execution_ops,
record_beneath_to_has_execution,
record_beneath_to_prepare_to_store): New variables.
(record_list_release_first_insn): Change function
record_list_release_first to this name.
(record_arch_list_cleanups): New function.
(record_message_cleanups): Removed.
(record_message): Change to call record_arch_list_cleanups
and record_list_release_first_insn.
(record_exec_entry): Add support for target core.
(record_open): Add support for target core.
(record_close): Add support for target core.
(record_kill): Add support for target core.
(record_registers_change): Call record_list_release_first_insn.
(record_fetch_registers): New function.
(record_prepare_to_store): New function.
(record_store_registers): Add support for target core.
(record_xfer_partial): Add support for target core.
(record_has_execution): New function.
(init_record_ops): Set record_ops.to_fetch_registers,
record_ops.to_prepare_to_store
and record_ops.to_has_execution.
(cmd_record_fd_cleanups): New function.
(cmd_record_dump): New function.
(cmd_record_load): New function.
(set_record_insn_max_num): Call record_list_release_first_insn.
(_initialize_record): Add commands "record dump"
and "record load".
---
record.c | 587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 563 insertions(+), 24 deletions(-)
--- a/record.c
+++ b/record.c
@@ -23,14 +23,22 @@
#include "gdbthread.h"
#include "event-top.h"
#include "exceptions.h"
+#include "completer.h"
+#include "arch-utils.h"
+#include "gdbcore.h"
+#include "exec.h"
#include "record.h"
+#include <byteswap.h>
#include <signal.h>
+#include <netinet/in.h>
#define DEFAULT_RECORD_INSN_MAX_NUM 200000
#define RECORD_IS_REPLAY \
- (record_list->next || execution_direction == EXEC_REVERSE)
+ (record_list->next || execution_direction == EXEC_REVERSE || record_core)
+
+#define RECORD_FILE_MAGIC htonl(0x20090726)
/* These are the core struct of record function.
@@ -76,9 +84,23 @@ struct record_entry
} u;
};
+struct record_core_buf_entry
+{
+ struct record_core_buf_entry *prev;
+ struct target_section *p;
+ bfd_byte *buf;
+};
+
/* This is the debug switch for process record. */
int record_debug = 0;
+/* Record with core target. */
+static int record_core = 0;
+static gdb_byte *record_core_regbuf;
+static struct target_section *record_core_start;
+static struct target_section *record_core_end;
+static struct record_core_buf_entry *record_core_buf_list = NULL;
+
/* These list is for execution log. */
static struct record_entry record_first;
static struct record_entry *record_list = &record_first;
@@ -101,6 +123,14 @@ static struct target_ops *record_beneath
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
struct target_waitstatus *,
int);
+static struct target_ops *record_beneath_to_fetch_registers_ops;
+static void (*record_beneath_to_fetch_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
static struct target_ops *record_beneath_to_store_registers_ops;
static void (*record_beneath_to_store_registers) (struct target_ops *,
struct regcache *,
@@ -117,6 +147,9 @@ static int (*record_beneath_to_insert_br
struct bp_target_info *);
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
struct bp_target_info *);
+static struct target_ops *record_beneath_to_has_execution_ops;
+static int (*record_beneath_to_has_execution) (struct target_ops *ops);
+static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache);
static void
record_list_release (struct record_entry *rec)
@@ -167,7 +200,7 @@ record_list_release_next (void)
}
static void
-record_list_release_first (void)
+record_list_release_first_insn (void)
{
struct record_entry *tmp = NULL;
enum record_type type;
@@ -338,30 +371,30 @@ record_check_insn_num (int set_terminal)
if (q)
record_stop_at_limit = 0;
else
- error (_("Process record: inferior program stopped."));
+ error (_("Process record: stoped by user."));
}
}
}
}
+static void
+record_arch_list_cleanups (void *ignore)
+{
+ record_list_release (record_arch_list_tail);
+}
+
/* Before inferior step (when GDB record the running message, inferior
only can step), GDB will call this function to record the values to
record_list. This function will call gdbarch_process_record to
record the running message of inferior and set them to
record_arch_list, and add it to record_list. */
-static void
-record_message_cleanups (void *ignore)
-{
- record_list_release (record_arch_list_tail);
-}
-
static int
record_message (void *args)
{
int ret;
struct regcache *regcache = args;
- struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
+ struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
@@ -384,7 +417,7 @@ record_message (void *args)
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
@@ -453,7 +486,8 @@ record_exec_entry (struct regcache *regc
if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
{
- if (execution_direction == EXEC_REVERSE)
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE && record_core))
{
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
@@ -473,7 +507,8 @@ record_exec_entry (struct regcache *regc
if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
entry->u.mem.len))
{
- if (execution_direction == EXEC_REVERSE)
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE &&
record_core))
{
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
@@ -505,8 +540,13 @@ record_open (char *name, int from_tty)
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+ if (!strcmp (current_target.to_shortname, "core"))
+ record_core = 1;
+ else
+ record_core = 0;
+
/* check exec */
- if (!target_has_execution)
+ if (!target_has_execution && !record_core)
error (_("Process record: the program is not being run."));
if (non_stop)
error (_("Process record target can't debug inferior in non-stop mode "
@@ -535,6 +575,8 @@ record_open (char *name, int from_tty)
record_beneath_to_xfer_partial = NULL;
record_beneath_to_insert_breakpoint = NULL;
record_beneath_to_remove_breakpoint = NULL;
+ record_beneath_to_has_execution = NULL;
+ record_beneath_to_prepare_to_store = NULL;
/* Set the beneath function pointers. */
for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -549,6 +591,11 @@ record_open (char *name, int from_tty)
record_beneath_to_wait = t->to_wait;
record_beneath_to_wait_ops = t;
}
+ if (!record_beneath_to_fetch_registers)
+ {
+ record_beneath_to_fetch_registers = t->to_fetch_registers;
+ record_beneath_to_fetch_registers_ops = t;
+ }
if (!record_beneath_to_store_registers)
{
record_beneath_to_store_registers = t->to_store_registers;
@@ -563,19 +610,51 @@ record_open (char *name, int from_tty)
record_beneath_to_insert_breakpoint = t->to_insert_breakpoint;
if (!record_beneath_to_remove_breakpoint)
record_beneath_to_remove_breakpoint = t->to_remove_breakpoint;
+ if (!record_beneath_to_has_execution)
+ {
+ record_beneath_to_has_execution_ops = t;
+ record_beneath_to_has_execution = t->to_has_execution;
+ }
+ if (!record_beneath_to_prepare_to_store)
+ record_beneath_to_prepare_to_store = t->to_prepare_to_store;
}
- if (!record_beneath_to_resume)
+ if (!record_beneath_to_resume && !record_core)
error (_("Process record can't get to_resume."));
- if (!record_beneath_to_wait)
+ if (!record_beneath_to_wait && !record_core)
error (_("Process record can't get to_wait."));
- if (!record_beneath_to_store_registers)
+ if (!record_beneath_to_fetch_registers)
+ error (_("Process record can't get to_fetch_registers."));
+ if (!record_beneath_to_store_registers && !record_core)
error (_("Process record can't get to_store_registers."));
if (!record_beneath_to_xfer_partial)
error (_("Process record can't get to_xfer_partial."));
- if (!record_beneath_to_insert_breakpoint)
+ if (!record_beneath_to_insert_breakpoint && !record_core)
error (_("Process record can't get to_insert_breakpoint."));
- if (!record_beneath_to_remove_breakpoint)
+ if (!record_beneath_to_remove_breakpoint && !record_core)
error (_("Process record can't get to_remove_breakpoint."));
+ if (!record_beneath_to_has_execution && !record_core)
+ error (_("Process record can't get to_has_execution."));
+ if (!record_beneath_to_prepare_to_store && !record_core)
+ error (_("Process record can't get to_prepare_to_store."));
+
+ if (record_core)
+ {
+ /* Get record_core_regbuf. */
+ struct regcache *regcache = get_current_regcache ();
+ int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ target_fetch_registers (regcache, -1);
+ record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
+ for (i = 0; i < regnum; i ++)
+ regcache_raw_collect (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+
+ /* Get record_core_start and record_core_end. */
+ if (build_section_table (core_bfd, &record_core_start, &record_core_end))
+ error (_("\"%s\": Can't find sections: %s"),
+ bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+ }
push_target (&record_ops);
@@ -588,10 +667,26 @@ record_open (char *name, int from_tty)
static void
record_close (int quitting)
{
+ struct record_core_buf_entry *entry;
+
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
record_list_release (record_list);
+
+ /* Release record_core_regbuf. */
+ xfree (record_core_regbuf);
+
+ /* Release record_core_buf_list. */
+ if (record_core_buf_list)
+ {
+ for (entry = record_core_buf_list->prev; entry; entry = entry->prev)
+ {
+ xfree (record_core_buf_list);
+ record_core_buf_list = entry;
+ }
+ record_core_buf_list = NULL;
+ }
}
static int record_resume_step = 0;
@@ -915,7 +1010,9 @@ record_kill (struct target_ops *ops)
fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
unpush_target (&record_ops);
- target_kill ();
+
+ if (!record_core)
+ target_kill ();
}
/* Record registers change (by user or by GDB) to list as an instruction. */
@@ -959,15 +1056,58 @@ record_registers_change (struct regcache
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
static void
+record_fetch_registers (struct target_ops *ops, struct regcache *regcache,
+ int regno)
+{
+ if (record_core)
+ {
+ if (regno < 0)
+ {
+ int num = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ for (i = 0; i < num; i ++)
+ regcache_raw_supply (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+ }
+ else
+ regcache_raw_supply (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+ }
+ else
+ record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops,
+ regcache, regno);
+}
+
+static void
+record_prepare_to_store (struct regcache *regcache)
+{
+ if (!record_core)
+ record_beneath_to_prepare_to_store (regcache);
+}
+
+static void
record_store_registers (struct target_ops *ops, struct regcache *regcache,
int regno)
{
+ if (record_core)
+ {
+ /* Debug with core. */
+ if (record_gdb_operation_disable)
+ regcache_raw_collect (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+ else
+ error (_("You can't do that without a process to debug."));
+
+ return;
+ }
+
if (!record_gdb_operation_disable)
{
if (RECORD_IS_REPLAY)
@@ -1014,6 +1154,7 @@ record_store_registers (struct target_op
record_registers_change (regcache, regno);
}
+
record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
regcache, regno);
}
@@ -1029,7 +1170,7 @@ record_xfer_partial (struct target_ops *
{
if (!record_gdb_operation_disable
&& (object == TARGET_OBJECT_MEMORY
- || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+ || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core)
{
if (RECORD_IS_REPLAY)
{
@@ -1073,11 +1214,91 @@ record_xfer_partial (struct target_ops *
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
+ if (record_core && object == TARGET_OBJECT_MEMORY)
+ {
+ /* Debug with core. */
+ if (record_gdb_operation_disable || !writebuf)
+ {
+ struct target_section *p;
+ for (p = record_core_start; p < record_core_end; p++)
+ {
+ if (offset >= p->addr)
+ {
+ struct record_core_buf_entry *entry;
+
+ if (offset >= p->endaddr)
+ continue;
+
+ if (offset + len > p->endaddr)
+ len = p->endaddr - offset;
+
+ offset -= p->addr;
+
+ /* Read readbuf or write writebuf p, offset, len. */
+ /* Check flags. */
+ if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
+ || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ if (readbuf)
+ memset (readbuf, 0, len);
+ return len;
+ }
+ /* Get record_core_buf_entry. */
+ for (entry = record_core_buf_list; entry;
+ entry = entry->prev)
+ if (entry->p == p)
+ break;
+ if (writebuf)
+ {
+ if (!entry)
+ {
+ /* Add a new entry. */
+ entry
+ = (struct record_core_buf_entry *)
+ xmalloc
+ (sizeof (struct record_core_buf_entry));
+ entry->p = p;
+ if (!bfd_malloc_and_get_section (p->bfd,
+ p->the_bfd_section,
+ &entry->buf))
+ {
+ xfree (entry);
+ return 0;
+ }
+ entry->prev = record_core_buf_list;
+ record_core_buf_list = entry;
+ }
+
+ memcpy (entry->buf + offset, writebuf, (size_t) len);
+ }
+ else
+ {
+ if (!entry)
+ return record_beneath_to_xfer_partial
+ (record_beneath_to_xfer_partial_ops,
+ object, annex, readbuf, writebuf,
+ offset, len);
+
+ memcpy (readbuf, entry->buf + offset, (size_t) len);
+ }
+
+ return len;
+ }
+ }
+
+ return 0;
+ }
+ else
+ error (_("You can't do that without a process to debug."));
+
+ return 0;
+ }
+
return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
object, annex, readbuf, writebuf,
offset, len);
@@ -1127,6 +1348,15 @@ record_can_execute_reverse (void)
return 1;
}
+int
+record_has_execution (struct target_ops *ops)
+{
+ if (record_core)
+ return 1;
+
+ return record_beneath_to_has_execution (ops);
+}
+
static void
init_record_ops (void)
{
@@ -1143,11 +1373,14 @@ init_record_ops (void)
record_ops.to_mourn_inferior = record_mourn_inferior;
record_ops.to_kill = record_kill;
record_ops.to_create_inferior = find_default_create_inferior;
+ record_ops.to_fetch_registers = record_fetch_registers;
+ record_ops.to_prepare_to_store = record_prepare_to_store;
record_ops.to_store_registers = record_store_registers;
record_ops.to_xfer_partial = record_xfer_partial;
record_ops.to_insert_breakpoint = record_insert_breakpoint;
record_ops.to_remove_breakpoint = record_remove_breakpoint;
record_ops.to_can_execute_reverse = record_can_execute_reverse;
+ record_ops.to_has_execution = record_has_execution;
record_ops.to_stratum = record_stratum;
record_ops.to_magic = OPS_MAGIC;
}
@@ -1168,6 +1401,300 @@ cmd_record_start (char *args, int from_t
execute_command ("target record", from_tty);
}
+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
+
+static inline void
+record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte)
+{
+ if (read (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to read dump of execution records in '%s'."),
+ recfilename);
+}
+
+static inline void
+record_write_dump (char *recfilename, int fildes, const void *buf,
+ size_t nbyte)
+{
+ if (write (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to write dump of execution records to '%s'."),
+ recfilename);
+}
+
+/* Dump the execution log to a file. */
+
+static void
+cmd_record_dump (char *args, int from_tty)
+{
+ char *recfilename, recfilename_buffer[40];
+ int recfd;
+ struct record_entry *cur_record_list;
+ uint32_t magic;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ struct cleanup *old_cleanups;
+ struct cleanup *set_cleanups;
+
+ if (current_target.to_stratum != record_stratum)
+ error (_("Process record is not started.\n"));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default corefile name is "gdb_record.PID". */
+ sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid));
+ recfilename = recfilename_buffer;
+ }
+
+ /* Open the dump file. */
+ recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for dump execution records: %s"),
+ recfilename, strerror (errno));
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Save the current record entry to "cur_record_list". */
+ cur_record_list = record_list;
+
+ /* Get the values of regcache and gdbarch. */
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+
+ /* Disable the GDB operation record. */
+ set_cleanups = record_gdb_operation_disable_set ();
+
+ /* Write the magic code. */
+ magic = RECORD_FILE_MAGIC;
+ record_write_dump (recfilename, recfd, &magic, 4);
+
+ /* Reverse execute to the begin of record list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == &record_first)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ /* Dump the entries to recfd and forward execute to the end of
+ record list. */
+ while (1)
+ {
+ /* Dump entry. */
+ if (record_list != &record_first)
+ {
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ tmpu8 = record_list->type;
+ record_write_dump (recfilename, recfd, &tmpu8, 1);
+
+ switch (record_list->type)
+ {
+ case record_reg: /* reg */
+ tmpu64 = record_list->u.reg.num;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd, record_list->u.reg.val,
+ MAX_REGISTER_SIZE);
+ break;
+ case record_mem: /* mem */
+ if (!record_list->u.mem.mem_entry_not_accessible)
+ {
+ tmpu64 = record_list->u.mem.addr;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ tmpu64 = record_list->u.mem.len;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd,
+ record_list->u.mem.val,
+ record_list->u.mem.len);
+ }
+ break;
+ }
+ }
+
+ /* Execute entry. */
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->next)
+ record_list = record_list->next;
+ else
+ break;
+ }
+
+ /* Reverse execute to cur_record_list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == cur_record_list)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ do_cleanups (set_cleanups);
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, _("Saved dump of execution "
+ "records to `%s'.\n"),
+ recfilename);
+}
+
+/* Load the execution log from a file. */
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+ int recfd;
+ uint32_t magic;
+ struct cleanup *old_cleanups;
+ struct cleanup *old_cleanups2;
+ struct record_entry *rec;
+ int insn_number = 0;
+
+ if (current_target.to_stratum != record_stratum)
+ {
+ cmd_record_start (NULL, from_tty);
+ printf_unfiltered (_("Auto start process record.\n"));
+ }
+
+ if (!args || (args && !*args))
+ error (_("Argument for filename required.\n"));
+
+ /* Open the load file. */
+ recfd = open (args, O_RDONLY | O_BINARY);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for loading execution records: %s"),
+ args, strerror (errno));
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Check the magic code. */
+ record_read_dump (args, recfd, &magic, 4);
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s' is not a valid dump of execution records."), args);
+
+ /* Load the entries in recfd to the record_arch_list_head and
+ record_arch_list_tail. */
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0);
+
+ while (1)
+ {
+ int ret;
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ ret = read (recfd, &tmpu8, 1);
+ if (ret < 0)
+ error (_("Failed to read dump of execution records in '%s'."), args);
+ if (ret == 0)
+ break;
+
+ switch (tmpu8)
+ {
+ case record_reg: /* reg */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+ /* Get num. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.reg.num = tmpu64;
+ /* Get val. */
+ record_read_dump (args, recfd, rec->u.reg.val, MAX_REGISTER_SIZE);
+ record_arch_list_add (rec);
+ break;
+ case record_mem: /* mem */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_mem;
+ /* Get addr. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.mem.addr = tmpu64;
+ /* Get len. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.mem.len = tmpu64;
+ rec->u.mem.mem_entry_not_accessible = 0;
+ rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len);
+ /* Get val. */
+ record_read_dump (args, recfd, rec->u.mem.val, rec->u.mem.len);
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+ record_arch_list_add (rec);
+ insn_number ++;
+ break;
+
+ default:
+ error (_("Format of '%s' is not right."), args);
+ break;
+ }
+ }
+
+ discard_cleanups (old_cleanups2);
+
+ /* Add record_arch_list_head to the end of record list. */
+ for (rec = record_list; rec->next; rec = rec->next);
+ rec->next = record_arch_list_head;
+ record_arch_list_head->prev = rec;
+
+ /* Update record_insn_num and record_insn_max_num. */
+ record_insn_num += insn_number;
+ if (record_insn_num > record_insn_max_num)
+ {
+ record_insn_max_num = record_insn_num;
+ warning (_("Auto increase record/replay buffer limit to %d."),
+ record_insn_max_num);
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+}
+
/* Truncate the record log from the present point
of replay until the end. */
@@ -1218,7 +1745,7 @@ set_record_insn_max_num (char *args, int
"the first ones?\n"));
while (record_insn_num > record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
}
}
@@ -1258,6 +1785,8 @@ info_record_command (char *args, int fro
void
_initialize_record (void)
{
+ struct cmd_list_element *c;
+
/* Init record_first. */
record_first.prev = NULL;
record_first.next = NULL;
@@ -1291,6 +1820,16 @@ _initialize_record (void)
"info record ", 0, &infolist);
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
+ c = add_cmd ("dump", class_obscure, cmd_record_dump,
+ _("Dump the execution records to a file.\n\
+Argument is optional filename. Default filename is
'gdb_record.<process_id>'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ c = add_cmd ("load", class_obscure, cmd_record_load,
+ _("Load previously dumped execution records from \
+a file given as argument."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
add_cmd ("delete", class_obscure, cmd_record_delete,
_("Delete the rest of execution log and start recording it anew."),
2009-08-03 Hui Zhu <teawater@gmail.com>
* gdb.texinfo (Process Record and Replay): Document the
"record dump" and "record dump" commands.
---
doc/gdb.texinfo | 15 +++++++++++++++
1 file changed, 15 insertions(+)
--- a/doc/gdb.texinfo
+++ b/doc/gdb.texinfo
@@ -5190,6 +5190,21 @@ When record target runs in replay mode (
subsequent execution log and begin to record a new execution log starting
from the current address. This means you will abandon the previously
recorded ``future'' and begin recording a new ``future''.
+
+@kindex record dump
+@kindex rec dump
+@item record dump [@var{file}]
+@itemx rec dump [@var{file}]
+Dump the execution records of the inferior process to a file. The optional
+argument @var{file} specifies the file name where to put the record dump.
+If not specified, the file name defaults to @file{gdb_record.@var{pid}}, where
+@var{pid} is is the PID of the inferior process.
+
+@kindex record load
+@kindex rec load
+@item record load @var{file}
+@itemx rec dump @var{file}
+Load previously-dumped execution records from @var{file}.
@end table
[-- Attachment #2: prec-dump.txt --]
[-- Type: text/plain, Size: 27569 bytes --]
---
record.c | 587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 563 insertions(+), 24 deletions(-)
--- a/record.c
+++ b/record.c
@@ -23,14 +23,22 @@
#include "gdbthread.h"
#include "event-top.h"
#include "exceptions.h"
+#include "completer.h"
+#include "arch-utils.h"
+#include "gdbcore.h"
+#include "exec.h"
#include "record.h"
+#include <byteswap.h>
#include <signal.h>
+#include <netinet/in.h>
#define DEFAULT_RECORD_INSN_MAX_NUM 200000
#define RECORD_IS_REPLAY \
- (record_list->next || execution_direction == EXEC_REVERSE)
+ (record_list->next || execution_direction == EXEC_REVERSE || record_core)
+
+#define RECORD_FILE_MAGIC htonl(0x20090726)
/* These are the core struct of record function.
@@ -76,9 +84,23 @@ struct record_entry
} u;
};
+struct record_core_buf_entry
+{
+ struct record_core_buf_entry *prev;
+ struct target_section *p;
+ bfd_byte *buf;
+};
+
/* This is the debug switch for process record. */
int record_debug = 0;
+/* Record with core target. */
+static int record_core = 0;
+static gdb_byte *record_core_regbuf;
+static struct target_section *record_core_start;
+static struct target_section *record_core_end;
+static struct record_core_buf_entry *record_core_buf_list = NULL;
+
/* These list is for execution log. */
static struct record_entry record_first;
static struct record_entry *record_list = &record_first;
@@ -101,6 +123,14 @@ static struct target_ops *record_beneath
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
struct target_waitstatus *,
int);
+static struct target_ops *record_beneath_to_fetch_registers_ops;
+static void (*record_beneath_to_fetch_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+ struct regcache *,
+ int regno);
static struct target_ops *record_beneath_to_store_registers_ops;
static void (*record_beneath_to_store_registers) (struct target_ops *,
struct regcache *,
@@ -117,6 +147,9 @@ static int (*record_beneath_to_insert_br
struct bp_target_info *);
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
struct bp_target_info *);
+static struct target_ops *record_beneath_to_has_execution_ops;
+static int (*record_beneath_to_has_execution) (struct target_ops *ops);
+static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache);
static void
record_list_release (struct record_entry *rec)
@@ -167,7 +200,7 @@ record_list_release_next (void)
}
static void
-record_list_release_first (void)
+record_list_release_first_insn (void)
{
struct record_entry *tmp = NULL;
enum record_type type;
@@ -338,30 +371,30 @@ record_check_insn_num (int set_terminal)
if (q)
record_stop_at_limit = 0;
else
- error (_("Process record: inferior program stopped."));
+ error (_("Process record: stoped by user."));
}
}
}
}
+static void
+record_arch_list_cleanups (void *ignore)
+{
+ record_list_release (record_arch_list_tail);
+}
+
/* Before inferior step (when GDB record the running message, inferior
only can step), GDB will call this function to record the values to
record_list. This function will call gdbarch_process_record to
record the running message of inferior and set them to
record_arch_list, and add it to record_list. */
-static void
-record_message_cleanups (void *ignore)
-{
- record_list_release (record_arch_list_tail);
-}
-
static int
record_message (void *args)
{
int ret;
struct regcache *regcache = args;
- struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
+ struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
record_arch_list_head = NULL;
record_arch_list_tail = NULL;
@@ -384,7 +417,7 @@ record_message (void *args)
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
@@ -453,7 +486,8 @@ record_exec_entry (struct regcache *regc
if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
{
- if (execution_direction == EXEC_REVERSE)
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE && record_core))
{
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
@@ -473,7 +507,8 @@ record_exec_entry (struct regcache *regc
if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
entry->u.mem.len))
{
- if (execution_direction == EXEC_REVERSE)
+ if ((execution_direction == EXEC_REVERSE && !record_core)
+ || (execution_direction != EXEC_REVERSE && record_core))
{
record_list->u.mem.mem_entry_not_accessible = 1;
if (record_debug)
@@ -505,8 +540,13 @@ record_open (char *name, int from_tty)
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+ if (!strcmp (current_target.to_shortname, "core"))
+ record_core = 1;
+ else
+ record_core = 0;
+
/* check exec */
- if (!target_has_execution)
+ if (!target_has_execution && !record_core)
error (_("Process record: the program is not being run."));
if (non_stop)
error (_("Process record target can't debug inferior in non-stop mode "
@@ -535,6 +575,8 @@ record_open (char *name, int from_tty)
record_beneath_to_xfer_partial = NULL;
record_beneath_to_insert_breakpoint = NULL;
record_beneath_to_remove_breakpoint = NULL;
+ record_beneath_to_has_execution = NULL;
+ record_beneath_to_prepare_to_store = NULL;
/* Set the beneath function pointers. */
for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -549,6 +591,11 @@ record_open (char *name, int from_tty)
record_beneath_to_wait = t->to_wait;
record_beneath_to_wait_ops = t;
}
+ if (!record_beneath_to_fetch_registers)
+ {
+ record_beneath_to_fetch_registers = t->to_fetch_registers;
+ record_beneath_to_fetch_registers_ops = t;
+ }
if (!record_beneath_to_store_registers)
{
record_beneath_to_store_registers = t->to_store_registers;
@@ -563,19 +610,51 @@ record_open (char *name, int from_tty)
record_beneath_to_insert_breakpoint = t->to_insert_breakpoint;
if (!record_beneath_to_remove_breakpoint)
record_beneath_to_remove_breakpoint = t->to_remove_breakpoint;
+ if (!record_beneath_to_has_execution)
+ {
+ record_beneath_to_has_execution_ops = t;
+ record_beneath_to_has_execution = t->to_has_execution;
+ }
+ if (!record_beneath_to_prepare_to_store)
+ record_beneath_to_prepare_to_store = t->to_prepare_to_store;
}
- if (!record_beneath_to_resume)
+ if (!record_beneath_to_resume && !record_core)
error (_("Process record can't get to_resume."));
- if (!record_beneath_to_wait)
+ if (!record_beneath_to_wait && !record_core)
error (_("Process record can't get to_wait."));
- if (!record_beneath_to_store_registers)
+ if (!record_beneath_to_fetch_registers)
+ error (_("Process record can't get to_fetch_registers."));
+ if (!record_beneath_to_store_registers && !record_core)
error (_("Process record can't get to_store_registers."));
if (!record_beneath_to_xfer_partial)
error (_("Process record can't get to_xfer_partial."));
- if (!record_beneath_to_insert_breakpoint)
+ if (!record_beneath_to_insert_breakpoint && !record_core)
error (_("Process record can't get to_insert_breakpoint."));
- if (!record_beneath_to_remove_breakpoint)
+ if (!record_beneath_to_remove_breakpoint && !record_core)
error (_("Process record can't get to_remove_breakpoint."));
+ if (!record_beneath_to_has_execution && !record_core)
+ error (_("Process record can't get to_has_execution."));
+ if (!record_beneath_to_prepare_to_store && !record_core)
+ error (_("Process record can't get to_prepare_to_store."));
+
+ if (record_core)
+ {
+ /* Get record_core_regbuf. */
+ struct regcache *regcache = get_current_regcache ();
+ int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ target_fetch_registers (regcache, -1);
+ record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
+ for (i = 0; i < regnum; i ++)
+ regcache_raw_collect (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+
+ /* Get record_core_start and record_core_end. */
+ if (build_section_table (core_bfd, &record_core_start, &record_core_end))
+ error (_("\"%s\": Can't find sections: %s"),
+ bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+ }
push_target (&record_ops);
@@ -588,10 +667,26 @@ record_open (char *name, int from_tty)
static void
record_close (int quitting)
{
+ struct record_core_buf_entry *entry;
+
if (record_debug)
fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
record_list_release (record_list);
+
+ /* Release record_core_regbuf. */
+ xfree (record_core_regbuf);
+
+ /* Release record_core_buf_list. */
+ if (record_core_buf_list)
+ {
+ for (entry = record_core_buf_list->prev; entry; entry = entry->prev)
+ {
+ xfree (record_core_buf_list);
+ record_core_buf_list = entry;
+ }
+ record_core_buf_list = NULL;
+ }
}
static int record_resume_step = 0;
@@ -915,7 +1010,9 @@ record_kill (struct target_ops *ops)
fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
unpush_target (&record_ops);
- target_kill ();
+
+ if (!record_core)
+ target_kill ();
}
/* Record registers change (by user or by GDB) to list as an instruction. */
@@ -959,15 +1056,58 @@ record_registers_change (struct regcache
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
static void
+record_fetch_registers (struct target_ops *ops, struct regcache *regcache,
+ int regno)
+{
+ if (record_core)
+ {
+ if (regno < 0)
+ {
+ int num = gdbarch_num_regs (get_regcache_arch (regcache));
+ int i;
+
+ for (i = 0; i < num; i ++)
+ regcache_raw_supply (regcache, i,
+ record_core_regbuf + MAX_REGISTER_SIZE * i);
+ }
+ else
+ regcache_raw_supply (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+ }
+ else
+ record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops,
+ regcache, regno);
+}
+
+static void
+record_prepare_to_store (struct regcache *regcache)
+{
+ if (!record_core)
+ record_beneath_to_prepare_to_store (regcache);
+}
+
+static void
record_store_registers (struct target_ops *ops, struct regcache *regcache,
int regno)
{
+ if (record_core)
+ {
+ /* Debug with core. */
+ if (record_gdb_operation_disable)
+ regcache_raw_collect (regcache, regno,
+ record_core_regbuf + MAX_REGISTER_SIZE * regno);
+ else
+ error (_("You can't do that without a process to debug."));
+
+ return;
+ }
+
if (!record_gdb_operation_disable)
{
if (RECORD_IS_REPLAY)
@@ -1014,6 +1154,7 @@ record_store_registers (struct target_op
record_registers_change (regcache, regno);
}
+
record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
regcache, regno);
}
@@ -1029,7 +1170,7 @@ record_xfer_partial (struct target_ops *
{
if (!record_gdb_operation_disable
&& (object == TARGET_OBJECT_MEMORY
- || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+ || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core)
{
if (RECORD_IS_REPLAY)
{
@@ -1073,11 +1214,91 @@ record_xfer_partial (struct target_ops *
record_list = record_arch_list_tail;
if (record_insn_num == record_insn_max_num && record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
else
record_insn_num++;
}
+ if (record_core && object == TARGET_OBJECT_MEMORY)
+ {
+ /* Debug with core. */
+ if (record_gdb_operation_disable || !writebuf)
+ {
+ struct target_section *p;
+ for (p = record_core_start; p < record_core_end; p++)
+ {
+ if (offset >= p->addr)
+ {
+ struct record_core_buf_entry *entry;
+
+ if (offset >= p->endaddr)
+ continue;
+
+ if (offset + len > p->endaddr)
+ len = p->endaddr - offset;
+
+ offset -= p->addr;
+
+ /* Read readbuf or write writebuf p, offset, len. */
+ /* Check flags. */
+ if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
+ || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ if (readbuf)
+ memset (readbuf, 0, len);
+ return len;
+ }
+ /* Get record_core_buf_entry. */
+ for (entry = record_core_buf_list; entry;
+ entry = entry->prev)
+ if (entry->p == p)
+ break;
+ if (writebuf)
+ {
+ if (!entry)
+ {
+ /* Add a new entry. */
+ entry
+ = (struct record_core_buf_entry *)
+ xmalloc
+ (sizeof (struct record_core_buf_entry));
+ entry->p = p;
+ if (!bfd_malloc_and_get_section (p->bfd,
+ p->the_bfd_section,
+ &entry->buf))
+ {
+ xfree (entry);
+ return 0;
+ }
+ entry->prev = record_core_buf_list;
+ record_core_buf_list = entry;
+ }
+
+ memcpy (entry->buf + offset, writebuf, (size_t) len);
+ }
+ else
+ {
+ if (!entry)
+ return record_beneath_to_xfer_partial
+ (record_beneath_to_xfer_partial_ops,
+ object, annex, readbuf, writebuf,
+ offset, len);
+
+ memcpy (readbuf, entry->buf + offset, (size_t) len);
+ }
+
+ return len;
+ }
+ }
+
+ return 0;
+ }
+ else
+ error (_("You can't do that without a process to debug."));
+
+ return 0;
+ }
+
return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
object, annex, readbuf, writebuf,
offset, len);
@@ -1127,6 +1348,15 @@ record_can_execute_reverse (void)
return 1;
}
+int
+record_has_execution (struct target_ops *ops)
+{
+ if (record_core)
+ return 1;
+
+ return record_beneath_to_has_execution (ops);
+}
+
static void
init_record_ops (void)
{
@@ -1143,11 +1373,14 @@ init_record_ops (void)
record_ops.to_mourn_inferior = record_mourn_inferior;
record_ops.to_kill = record_kill;
record_ops.to_create_inferior = find_default_create_inferior;
+ record_ops.to_fetch_registers = record_fetch_registers;
+ record_ops.to_prepare_to_store = record_prepare_to_store;
record_ops.to_store_registers = record_store_registers;
record_ops.to_xfer_partial = record_xfer_partial;
record_ops.to_insert_breakpoint = record_insert_breakpoint;
record_ops.to_remove_breakpoint = record_remove_breakpoint;
record_ops.to_can_execute_reverse = record_can_execute_reverse;
+ record_ops.to_has_execution = record_has_execution;
record_ops.to_stratum = record_stratum;
record_ops.to_magic = OPS_MAGIC;
}
@@ -1168,6 +1401,300 @@ cmd_record_start (char *args, int from_t
execute_command ("target record", from_tty);
}
+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
+
+static inline void
+record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte)
+{
+ if (read (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to read dump of execution records in '%s'."),
+ recfilename);
+}
+
+static inline void
+record_write_dump (char *recfilename, int fildes, const void *buf,
+ size_t nbyte)
+{
+ if (write (fildes, buf, nbyte) != nbyte)
+ error (_("Failed to write dump of execution records to '%s'."),
+ recfilename);
+}
+
+/* Dump the execution log to a file. */
+
+static void
+cmd_record_dump (char *args, int from_tty)
+{
+ char *recfilename, recfilename_buffer[40];
+ int recfd;
+ struct record_entry *cur_record_list;
+ uint32_t magic;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ struct cleanup *old_cleanups;
+ struct cleanup *set_cleanups;
+
+ if (current_target.to_stratum != record_stratum)
+ error (_("Process record is not started.\n"));
+
+ if (args && *args)
+ recfilename = args;
+ else
+ {
+ /* Default corefile name is "gdb_record.PID". */
+ sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid));
+ recfilename = recfilename_buffer;
+ }
+
+ /* Open the dump file. */
+ recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for dump execution records: %s"),
+ recfilename, strerror (errno));
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Save the current record entry to "cur_record_list". */
+ cur_record_list = record_list;
+
+ /* Get the values of regcache and gdbarch. */
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+
+ /* Disable the GDB operation record. */
+ set_cleanups = record_gdb_operation_disable_set ();
+
+ /* Write the magic code. */
+ magic = RECORD_FILE_MAGIC;
+ record_write_dump (recfilename, recfd, &magic, 4);
+
+ /* Reverse execute to the begin of record list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == &record_first)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ /* Dump the entries to recfd and forward execute to the end of
+ record list. */
+ while (1)
+ {
+ /* Dump entry. */
+ if (record_list != &record_first)
+ {
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ tmpu8 = record_list->type;
+ record_write_dump (recfilename, recfd, &tmpu8, 1);
+
+ switch (record_list->type)
+ {
+ case record_reg: /* reg */
+ tmpu64 = record_list->u.reg.num;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd, record_list->u.reg.val,
+ MAX_REGISTER_SIZE);
+ break;
+ case record_mem: /* mem */
+ if (!record_list->u.mem.mem_entry_not_accessible)
+ {
+ tmpu64 = record_list->u.mem.addr;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ tmpu64 = record_list->u.mem.len;
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd,
+ record_list->u.mem.val,
+ record_list->u.mem.len);
+ }
+ break;
+ }
+ }
+
+ /* Execute entry. */
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->next)
+ record_list = record_list->next;
+ else
+ break;
+ }
+
+ /* Reverse execute to cur_record_list. */
+ while (1)
+ {
+ /* Check for beginning and end of log. */
+ if (record_list == cur_record_list)
+ break;
+
+ record_exec_entry (regcache, gdbarch, record_list);
+
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+
+ do_cleanups (set_cleanups);
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, _("Saved dump of execution "
+ "records to `%s'.\n"),
+ recfilename);
+}
+
+/* Load the execution log from a file. */
+
+static void
+cmd_record_load (char *args, int from_tty)
+{
+ int recfd;
+ uint32_t magic;
+ struct cleanup *old_cleanups;
+ struct cleanup *old_cleanups2;
+ struct record_entry *rec;
+ int insn_number = 0;
+
+ if (current_target.to_stratum != record_stratum)
+ {
+ cmd_record_start (NULL, from_tty);
+ printf_unfiltered (_("Auto start process record.\n"));
+ }
+
+ if (!args || (args && !*args))
+ error (_("Argument for filename required.\n"));
+
+ /* Open the load file. */
+ recfd = open (args, O_RDONLY | O_BINARY);
+ if (recfd < 0)
+ error (_("Failed to open '%s' for loading execution records: %s"),
+ args, strerror (errno));
+ old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd);
+
+ /* Check the magic code. */
+ record_read_dump (args, recfd, &magic, 4);
+ if (magic != RECORD_FILE_MAGIC)
+ error (_("'%s' is not a valid dump of execution records."), args);
+
+ /* Load the entries in recfd to the record_arch_list_head and
+ record_arch_list_tail. */
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0);
+
+ while (1)
+ {
+ int ret;
+ uint8_t tmpu8;
+ uint64_t tmpu64;
+
+ ret = read (recfd, &tmpu8, 1);
+ if (ret < 0)
+ error (_("Failed to read dump of execution records in '%s'."), args);
+ if (ret == 0)
+ break;
+
+ switch (tmpu8)
+ {
+ case record_reg: /* reg */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+ /* Get num. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.reg.num = tmpu64;
+ /* Get val. */
+ record_read_dump (args, recfd, rec->u.reg.val, MAX_REGISTER_SIZE);
+ record_arch_list_add (rec);
+ break;
+ case record_mem: /* mem */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_mem;
+ /* Get addr. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.mem.addr = tmpu64;
+ /* Get len. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+#endif
+ rec->u.mem.len = tmpu64;
+ rec->u.mem.mem_entry_not_accessible = 0;
+ rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len);
+ /* Get val. */
+ record_read_dump (args, recfd, rec->u.mem.val, rec->u.mem.len);
+ record_arch_list_add (rec);
+ break;
+
+ case record_end: /* end */
+ rec = (struct record_entry *) xmalloc (sizeof (struct record_entry));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+ record_arch_list_add (rec);
+ insn_number ++;
+ break;
+
+ default:
+ error (_("Format of '%s' is not right."), args);
+ break;
+ }
+ }
+
+ discard_cleanups (old_cleanups2);
+
+ /* Add record_arch_list_head to the end of record list. */
+ for (rec = record_list; rec->next; rec = rec->next);
+ rec->next = record_arch_list_head;
+ record_arch_list_head->prev = rec;
+
+ /* Update record_insn_num and record_insn_max_num. */
+ record_insn_num += insn_number;
+ if (record_insn_num > record_insn_max_num)
+ {
+ record_insn_max_num = record_insn_num;
+ warning (_("Auto increase record/replay buffer limit to %d."),
+ record_insn_max_num);
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* Succeeded. */
+ fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args);
+}
+
/* Truncate the record log from the present point
of replay until the end. */
@@ -1218,7 +1745,7 @@ set_record_insn_max_num (char *args, int
"the first ones?\n"));
while (record_insn_num > record_insn_max_num)
- record_list_release_first ();
+ record_list_release_first_insn ();
}
}
@@ -1258,6 +1785,8 @@ info_record_command (char *args, int fro
void
_initialize_record (void)
{
+ struct cmd_list_element *c;
+
/* Init record_first. */
record_first.prev = NULL;
record_first.next = NULL;
@@ -1291,6 +1820,16 @@ _initialize_record (void)
"info record ", 0, &infolist);
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
+ c = add_cmd ("dump", class_obscure, cmd_record_dump,
+ _("Dump the execution records to a file.\n\
+Argument is optional filename. Default filename is 'gdb_record.<process_id>'."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
+ c = add_cmd ("load", class_obscure, cmd_record_load,
+ _("Load previously dumped execution records from \
+a file given as argument."),
+ &record_cmdlist);
+ set_cmd_completer (c, filename_completer);
add_cmd ("delete", class_obscure, cmd_record_delete,
_("Delete the rest of execution log and start recording it anew."),
[-- Attachment #3: prec-dump-doc.txt --]
[-- Type: text/plain, Size: 951 bytes --]
---
doc/gdb.texinfo | 15 +++++++++++++++
1 file changed, 15 insertions(+)
--- a/doc/gdb.texinfo
+++ b/doc/gdb.texinfo
@@ -5190,6 +5190,21 @@ When record target runs in replay mode (
subsequent execution log and begin to record a new execution log starting
from the current address. This means you will abandon the previously
recorded ``future'' and begin recording a new ``future''.
+
+@kindex record dump
+@kindex rec dump
+@item record dump [@var{file}]
+@itemx rec dump [@var{file}]
+Dump the execution records of the inferior process to a file. The optional
+argument @var{file} specifies the file name where to put the record dump.
+If not specified, the file name defaults to @file{gdb_record.@var{pid}}, where
+@var{pid} is is the PID of the inferior process.
+
+@kindex record load
+@kindex rec load
+@item record load @var{file}
+@itemx rec dump @var{file}
+Load previously-dumped execution records from @var{file}.
@end table
next prev parent reply other threads:[~2009-08-03 4:12 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-01 7:31 Hui Zhu
2009-08-01 9:57 ` Eli Zaretskii
2009-08-01 19:20 ` Michael Snyder
2009-08-02 3:18 ` Michael Snyder
2009-08-02 5:58 ` Hui Zhu
2009-08-03 4:12 ` Hui Zhu [this message]
2009-08-03 18:29 ` Eli Zaretskii
2009-08-04 1:58 ` Hui Zhu
2009-08-04 2:07 ` Hui Zhu
2009-08-04 18:26 ` Eli Zaretskii
2009-08-04 20:01 ` Michael Snyder
2009-08-05 9:21 ` Hui Zhu
2009-08-05 20:19 ` [RFA/RFC] Add dump and load command to process record (file format etc) Michael Snyder
2009-08-06 2:17 ` Hui Zhu
2009-08-12 14:11 ` Michael Snyder
2009-08-12 15:16 ` Tom Tromey
2009-08-12 22:38 ` Michael Snyder
2009-08-16 0:04 ` Hui Zhu
2009-08-05 21:23 ` [RFA/RFC] Add dump and load command to process record and replay Michael Snyder
2009-08-06 3:14 ` Eli Zaretskii
2009-08-06 14:16 ` Hui Zhu
2009-08-07 3:27 ` Michael Snyder
2009-08-07 3:29 ` Hui Zhu
2009-08-07 3:34 ` Michael Snyder
2009-08-07 4:06 ` Hui Zhu
2009-08-07 8:41 ` Eli Zaretskii
2009-08-07 9:53 ` Hui Zhu
2009-08-07 12:51 ` Eli Zaretskii
2009-08-07 16:22 ` Hui Zhu
2009-08-07 17:42 ` Michael Snyder
2009-08-08 13:28 ` Hui Zhu
2009-08-10 3:09 ` Michael Snyder
2009-08-22 17:39 ` Hui Zhu
2009-08-23 1:14 ` Hui Zhu
2009-08-23 23:43 ` Michael Snyder
2009-08-24 8:20 ` Hui Zhu
2009-08-24 18:32 ` Michael Snyder
2009-08-25 8:47 ` Hui Zhu
2009-08-26 1:40 ` Michael Snyder
2009-08-26 2:59 ` Michael Snyder
2009-08-29 15:53 ` Hui Zhu
2009-08-29 18:06 ` Eli Zaretskii
2009-08-29 18:28 ` Hui Zhu
2009-08-29 20:26 ` Eli Zaretskii
2009-08-29 20:39 ` Michael Snyder
2009-08-30 3:03 ` Eli Zaretskii
2009-08-30 5:36 ` Hui Zhu
2009-08-30 23:40 ` Eli Zaretskii
2009-08-31 7:10 ` Hui Zhu
2009-09-05 3:30 ` Michael Snyder
2009-09-06 16:29 ` Hui Zhu
2009-08-29 20:05 ` Michael Snyder
2009-08-29 20:33 ` Eli Zaretskii
2009-08-29 21:20 ` Michael Snyder
2022-01-21 6:46 Simon Sobisch via Gdb-patches
2022-01-24 9:26 ` Hui Zhu via Gdb-patches
2022-04-13 12:21 ` Simon Sobisch via Gdb-patches
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=daef60380908022111t1023a92cnf0dc89ff7883a0b5@mail.gmail.com \
--to=teawater@gmail.com \
--cc=eliz@gnu.org \
--cc=gdb-patches@sourceware.org \
--cc=msnyder@vmware.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox