This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [RFA/RFC] Add dump and load command to process record and replay
Hui Zhu wrote:
+static void
+cmd_record_fd_cleanups (void *recfdp)
+{
+ int recfd = *(int *) recfdp;
+ close (recfd);
+}
Here's a suggested comment to start documenting the file format:
/* Record log save-file format
Version 1
Header:
4 bytes: magic number RECORD_FILE_MAGIC.
NOTE: be sure to change whenever this file format changes!
Records:
record_end:
1 byte: record type (record_end)
record_reg:
1 byte: record type (record_reg)
8 bytes: register id
16 bytes: register value
record_mem:
1 byte: record type (record_mem)
8 bytes: memory address
8 bytes: memory length
n bytes: memory value (n == memory length)
Version 2 (proposed)
[...] */
Below I'll suggest some possible debugging printfs.
Feel free to modify them to your own liking.
+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. */
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
_("Saving recording to file '%s'\n"),
recfilename);
+ 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;
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"),
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);
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Writing register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
p->u.reg.num,
*(ULONGEST *) p->u.reg.val,
MAX_REGISTER_SIZE);
+ 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);
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Writing memory 0x%08x (1 plus 8 plus 8 bytes plus %d bytes)\n"),
(unsigned int) p->u.mem.addr,
p->u.mem.len);
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ tmpu64 = record_list->u.mem.len;
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ record_write_dump (recfilename, recfd, &tmpu64, 8);
+
+ record_write_dump (recfilename, recfd,
+ record_list->u.mem.val,
+ record_list->u.mem.len);
+ }
+ break;
case record_end:
/* FIXME: record the contents of record_end rec. */
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Writing record_end (1 byte)\n"));
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. */
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
_("Restoring recording from file '%s'\n"), args);
+ /* 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);
+ rec->u.reg.num = tmpu64;
+ /* Get val. */
+ record_read_dump (args, recfd, rec->u.reg.val, MAX_REGISTER_SIZE);
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"),
rec->u.reg.num,
*(ULONGEST *) 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);
+ rec->u.mem.addr = tmpu64;
+ /* Get len. */
+ record_read_dump (args, recfd, &tmpu64, 8);
+ if (BYTE_ORDER == LITTLE_ENDIAN)
+ tmpu64 = bswap_64 (tmpu64);
+ 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);
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"),
(unsigned int) rec->u.mem.addr,
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;
if (record_debug)
fprintf_unfiltered (gdb_stdlog, _("\
Reading record_end (one byte)\n"));
+ 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);
+}
+