This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFA/RFC] Add dump and load command to process record and replay


And about record load.

I keep it because "record" is a prefix_cmd (If it's not,  I can let
"record" command load file directly).  A file name cannot be the
argument of this command.  So I keep the command record load.

Thanks,
Hui

On Sun, Aug 23, 2009 at 01:34, Hui Zhu<teawater@gmail.com> wrote:
> On Mon, Aug 10, 2009 at 06:56, Michael Snyder<msnyder@vmware.com> wrote:
>> Hui Zhu wrote:
>>>
>>> I think give him a query is very clear.
>>>
>>> When he load, if there are some record log, it will query to user. ?He
>>> must choice remove the old record log or keep them. ?He already know
>>> what will happen.
>>
>> This is my opinion. ?The default should be to remove the old log
>> (not to query). ?I think this will be both the most common case
>> and the safest. ?We can maybe add a command option for those who
>> wish not to do that.
>>
>> Anyone else have an opinion?
>>
>>> On Sat, Aug 8, 2009 at 01:20, Michael Snyder<msnyder@vmware.com> wrote:
>>>>
>>>> Eli Zaretskii wrote:
>>>>>>
>>>>>> From: Hui Zhu <teawater@gmail.com>
>>>>>> Date: Fri, 7 Aug 2009 11:34:20 +0800
>>>>>> Cc: Eli Zaretskii <eliz@gnu.org>, "gdb-patches@sourceware.org"
>>>>>> <gdb-patches@sourceware.org>
>>>>>>
>>>>>> I think a warning is clear to most of people.
>>>>>>
>>>>>> And when he get this warning. ?He can delete the record list and load
>>>>>> again. ?He will lost nothing.
>>>>>>
>>>>>> If we delete the old record list, maybe he still need old record. ?He
>>>>>> will lost something.
>>>>>
>>>>> Instead of a warning, how about asking the user whether to discard the
>>>>> old records or keep them?
>>>>
>>>> My concern is, in most cases keeping them will be the wrong thing to do.
>>>> It will be very easy to create an internally inconsistent state, and
>>>> rather unlikely to create one that is *not* internally inconsistant.
>>>>
>>>> Think about it -- we will be concatenating two independent sets of
>>>> state changes, with no way of knowing that the actual machine state
>>>> at the end of one is the same as the machine state at the beginning
>>>> of the other. ?When these are then replayed, their effect may have
>>>> little or nothing to do with what the real machine would actually do.
>>>>
>>>> To actually get this right, you would have to be *sure* that your
>>>> target machine is in the exact same state "now" (ie. when you do
>>>> the load command) as it was at the *beginning* of the previous
>>>> recording/debugging session.
>>>>
>>>> I would rather either make this a separate, "expert mode"
>>>> command, or better still, leave it for a future patch to extend
>>>> the basic (and safe) patch that we first accept.
>>>>
>>>>
>>>>
>>>
>>
>>
>
> Hi Michael,
>
> I make a new version patch. ?It has a lot of changes.
> Remove record_core and add a new target record_core for core target to
> make the code more clear.
> Make the load together with record_open.
>
> Please help me review it.
>
> Thanks,
> Hui
>
> 2009-08-23 ?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_FILE_MAGIC): New macro.
> ? ? ? ?(record_core_buf_entry): New struct.
> ? ? ? ?(record_core_ops): New target_ops.
> ? ? ? ?(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, (record_read_dump, record_fd_cleanups,
> ? ? ? ?record_load, record_core_open_1, record_open_1): New function.
> ? ? ? ?(record_open): Add support for record_core_ops.
> ? ? ? ?(record_close): Add support for record_core_ops.
> ? ? ? ?(record_wait): Call function 'record_exec_entry' and
> ? ? ? ?add support for target core.
> ? ? ? ?(record_registers_change): Call record_list_release_first_insn.
> ? ? ? ?(record_core_resume, record_core_resume, record_core_kill,
> ? ? ? ?record_core_fetch_registers, record_core_prepare_to_store,
> ? ? ? ?record_core_store_registers, record_core_xfer_partial,
> ? ? ? ?record_core_insert_breakpoint, record_core_remove_breakpoint,
> ? ? ? ?record_core_has_execution, init_record_core_ops,
> ? ? ? ?cmd_record_load, record_write_dump,
> ? ? ? ?cmd_record_dump): New function.
> ? ? ? ?(cmd_record_stop): Add support for record_core_ops.
> ? ? ? ?(set_record_insn_max_num): Call record_list_release_first_insn.
> ? ? ? ?(_initialize_record): Add commands "record dump"
> ? ? ? ?and "record load".
>
> ---
> ?record.c | ?970 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
> ?1 file changed, 844 insertions(+), 126 deletions(-)
>
> --- a/record.c
> +++ b/record.c
> @@ -23,15 +23,23 @@
> ?#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)
>
> +#define RECORD_FILE_MAGIC ? ? ?htonl(0x20090726)
> +
> ?/* These are the core struct of record function.
>
> ? ?An record_entry is a record of the value change of a register
> @@ -78,9 +86,22 @@ 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 buf with core target. ?*/
> +static gdb_byte *record_core_regbuf = NULL;
> +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;
> @@ -94,6 +115,7 @@ static int record_insn_num = 0;
>
> ?/* The target_ops of process record. ?*/
> ?static struct target_ops record_ops;
> +static struct target_ops record_core_ops;
>
> ?/* The beneath function pointers. ?*/
> ?static struct target_ops *record_beneath_to_resume_ops;
> @@ -169,7 +191,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;
> @@ -340,30 +362,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;
> @@ -386,7 +408,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++;
>
> @@ -416,13 +438,309 @@ record_gdb_operation_disable_set (void)
> ? return old_cleanups;
> ?}
>
> +static inline void
> +record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
> + ? ? ? ? ? ? ? ? ? struct record_entry *entry, int core)
> +{
> + ?switch (entry->type)
> + ? ?{
> + ? ?case record_reg: /* reg */
> + ? ? ?{
> + ? ? ? ?gdb_byte reg[MAX_REGISTER_SIZE];
> +
> + ? ? ? ?if (record_debug > 1)
> + ? ? ? ? ?fprintf_unfiltered (gdb_stdlog,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"Process record: record_reg %s to "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"inferior num = %d.\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?host_address_to_string (entry),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?entry->u.reg.num);
> +
> + ? ? ? ?regcache_cooked_read (regcache, entry->u.reg.num, reg);
> + ? ? ? ?regcache_cooked_write (regcache, entry->u.reg.num, entry->u.reg.val);
> + ? ? ? ?memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE);
> + ? ? ?}
> + ? ? ?break;
> +
> + ? ?case record_mem: /* mem */
> + ? ? ?{
> + ? ? ? ?if (!record_list->u.mem.mem_entry_not_accessible)
> + ? ? ? ? ?{
> + ? ? ? ? ? ?gdb_byte *mem = alloca (entry->u.mem.len);
> +
> + ? ? ? ? ? ?if (record_debug > 1)
> + ? ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"Process record: record_mem %s to "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"inferior addr = %s len = %d.\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?host_address_to_string (entry),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?paddress (gdbarch, entry->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.len);
> +
> + ? ? ? ? ? ?if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
> + ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ? if ((execution_direction == EXEC_REVERSE && !core)
> + ? ? ? ? ? ? ? ? ? ? || (execution_direction != EXEC_REVERSE && core))
> + ? ? ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ? ? ?record_list->u.mem.mem_entry_not_accessible = 1;
> + ? ? ? ? ? ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ? ? ? ? ? ?warning (_("Process record: error reading memory at "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "addr = %s len = %d."),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? paddress (gdbarch, entry->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? entry->u.mem.len);
> + ? ? ? ? ? ? ? ? ?}
> + ? ? ? ? ? ? ? ?else
> + ? ? ? ? ? ? ? ? ?error (_("Process record: error reading memory at "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? "addr = %s len = %d."),
> + ? ? ? ? ? ? ? ? ? ? ? ? paddress (gdbarch, entry->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ?entry->u.mem.len);
> + ? ? ? ? ? ? ?}
> + ? ? ? ? ? ?else
> + ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ?if (target_write_memory (entry->u.mem.addr, entry->u.mem.val,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? entry->u.mem.len))
> + ? ? ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ? ? ? if ((execution_direction == EXEC_REVERSE && !core)
> + ? ? ? ? ? ? ? ? ? ? ? ? || (execution_direction != EXEC_REVERSE && core))
> + ? ? ? ? ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.mem_entry_not_accessible = 1;
> + ? ? ? ? ? ? ? ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ? ? ? ? ? ? ? ?warning (_("Process record: error writing memory at "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "addr = %s len = %d."),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? paddress (gdbarch, entry->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? entry->u.mem.len);
> + ? ? ? ? ? ? ? ? ? ? ?}
> + ? ? ? ? ? ? ? ? ? ?else
> + ? ? ? ? ? ? ? ? ? ? ?error (_("Process record: error writing memory at "
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "addr = %s len = %d."),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? paddress (gdbarch, entry->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?entry->u.mem.len);
> + ? ? ? ? ? ? ? ? ?}
> + ? ? ? ? ? ? ?}
> +
> + ? ? ? ? ? ?memcpy (entry->u.mem.val, mem, entry->u.mem.len);
> + ? ? ? ? ?}
> + ? ? ?}
> + ? ? ?break;
> + ? ?}
> +}
> +
> +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 void
> -record_open (char *name, int from_tty)
> +record_fd_cleanups (void *recfdp)
> ?{
> - ?struct target_ops *t;
> + ?int recfd = *(int *) recfdp;
> + ?close (recfd);
> +}
>
> - ?if (record_debug)
> - ? ?fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
> +/* Load the execution log from a file. ?*/
> +
> +static void
> +record_load (char *name)
> +{
> + ?int recfd;
> + ?uint32_t magic;
> + ?struct cleanup *old_cleanups;
> + ?struct cleanup *old_cleanups2;
> + ?struct record_entry *rec;
> + ?int insn_number = 0;
> +
> + ?if (!name || (name && !*name))
> + ? ?return;
> +
> + ?/* Open the load file. ?*/
> + ?recfd = open (name, O_RDONLY | O_BINARY);
> + ?if (recfd < 0)
> + ? ?error (_("Failed to open '%s' for loading execution records: %s"),
> + ? ? ? ? ? name, strerror (errno));
> + ?old_cleanups = make_cleanup (record_fd_cleanups, &recfd);
> +
> + ?/* Check the magic code. ?*/
> + ?record_read_dump (name, recfd, &magic, 4);
> + ?if (magic != RECORD_FILE_MAGIC)
> + ? ?error (_("'%s' is not a valid dump of execution records."), name);
> +
> + ?/* 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'."), name);
> + ? ? ?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 (name, recfd, &tmpu64, 8);
> + ? ? ? ? ?if (BYTE_ORDER == LITTLE_ENDIAN)
> + ? ? ? ? ? ?tmpu64 = bswap_64 (tmpu64);
> + ? ? ? ? ?rec->u.reg.num = tmpu64;
> +
> + ? ? ? ? ?/* Get val. ?*/
> + ? ? ? ? ?record_read_dump (name, recfd, rec->u.reg.val, MAX_REGISTER_SIZE);
> +
> + ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog, _("\
> +Reading register %d (1 plus 8 plus %d bytes)\n"),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rec->u.reg.num,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?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 (name, recfd, &tmpu64, 8);
> + ? ? ? ? ?if (BYTE_ORDER == LITTLE_ENDIAN)
> + ? ? ? ? ? ?tmpu64 = bswap_64 (tmpu64);
> + ? ? ? ? ?rec->u.mem.addr = tmpu64;
> +
> + ? ? ? ? ?/* Get len. ?*/
> + ? ? ? ? ?record_read_dump (name, 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 (name, recfd, rec->u.mem.val, rec->u.mem.len);
> +
> + ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog, _("\
> +Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?paddress (get_current_arch (),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rec->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rec->u.mem.len);
> +
> + ? ? ? ? ?record_arch_list_add (rec);
> + ? ? ? ? ?break;
> +
> + ? ? ? ?case record_end: /* end */
> + ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?_("Reading record_end (1 byte)\n"));
> +
> + ? ? ? ? ?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."), name);
> + ? ? ? ? ?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", name);
> +}
> +
> +static struct target_ops *tmp_to_resume_ops;
> +static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?enum target_signal);
> +static struct target_ops *tmp_to_wait_ops;
> +static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct target_waitstatus *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int);
> +static struct target_ops *tmp_to_store_registers_ops;
> +static void (*tmp_to_store_registers) (struct target_ops *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct regcache *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int regno);
> +static struct target_ops *tmp_to_xfer_partial_ops;
> +static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? enum target_object object,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *annex,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gdb_byte *readbuf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const gdb_byte *writebuf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ULONGEST offset,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? LONGEST len);
> +static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct bp_target_info *);
> +static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct bp_target_info *);
> +
> +static void
> +record_core_open_1 (char *name, int from_tty)
> +{
> + ?struct regcache *regcache = get_current_regcache ();
> + ?int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
> + ?int i;
> +
> + ?if (!name || (name && !*name))
> + ? ?error (_("Argument for gdb record filename required.\n"));
> +
> + ?/* Get record_core_regbuf. ?*/
> + ?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))
> + ? ?{
> + ? ? ?xfree (record_core_regbuf);
> + ? ? ?record_core_regbuf = NULL;
> + ? ? ?error (_("\"%s\": Can't find sections: %s"),
> + ? ? ? ? ? ? bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
> + ? ?}
> +
> + ?push_target (&record_core_ops);
> +}
> +
> +static void
> +record_open_1 (char *name, int from_tty)
> +{
> + ?struct target_ops *t;
>
> ? /* check exec */
> ? if (!target_has_execution)
> @@ -438,6 +756,28 @@ record_open (char *name, int from_tty)
> ? ? error (_("Process record: the current architecture doesn't support "
> ? ? ? ? ? ? "record function."));
>
> + ?if (!tmp_to_resume)
> + ? ?error (_("Process record can't get to_resume."));
> + ?if (!tmp_to_wait)
> + ? ?error (_("Process record can't get to_wait."));
> + ?if (!tmp_to_store_registers)
> + ? ?error (_("Process record can't get to_store_registers."));
> + ?if (!tmp_to_insert_breakpoint)
> + ? ?error (_("Process record can't get to_insert_breakpoint."));
> + ?if (!tmp_to_remove_breakpoint)
> + ? ?error (_("Process record can't get to_remove_breakpoint."));
> +
> + ?push_target (&record_ops);
> +}
> +
> +static void
> +record_open (char *name, int from_tty)
> +{
> + ?struct target_ops *t;
> +
> + ?if (record_debug)
> + ? ?fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
> +
> ? /* Check if record target is already running. ?*/
> ? if (current_target.to_stratum == record_stratum)
> ? ? {
> @@ -447,70 +787,102 @@ record_open (char *name, int from_tty)
> ? ? ? ?return;
> ? ? }
>
> - ?/*Reset the beneath function pointers. ?*/
> - ?record_beneath_to_resume = NULL;
> - ?record_beneath_to_wait = NULL;
> - ?record_beneath_to_store_registers = NULL;
> - ?record_beneath_to_xfer_partial = NULL;
> - ?record_beneath_to_insert_breakpoint = NULL;
> - ?record_beneath_to_remove_breakpoint = NULL;
> + ?/* Reset the tmp beneath pointers. ?*/
> + ?tmp_to_resume_ops = NULL;
> + ?tmp_to_resume = NULL;
> + ?tmp_to_wait_ops = NULL;
> + ?tmp_to_wait = NULL;
> + ?tmp_to_store_registers_ops = NULL;
> + ?tmp_to_store_registers = NULL;
> + ?tmp_to_xfer_partial_ops = NULL;
> + ?tmp_to_xfer_partial = NULL;
> + ?tmp_to_insert_breakpoint = NULL;
> + ?tmp_to_remove_breakpoint = NULL;
>
> ? /* Set the beneath function pointers. ?*/
> ? for (t = current_target.beneath; t != NULL; t = t->beneath)
> ? ? {
> - ? ? ?if (!record_beneath_to_resume)
> + ? ? ?if (!tmp_to_resume)
> ? ? ? ? {
> - ? ? ? ? record_beneath_to_resume = t->to_resume;
> - ? ? ? ? record_beneath_to_resume_ops = t;
> + ? ? ? ? tmp_to_resume = t->to_resume;
> + ? ? ? ? tmp_to_resume_ops = t;
> ? ? ? ? }
> - ? ? ?if (!record_beneath_to_wait)
> + ? ? ?if (!tmp_to_wait)
> ? ? ? ? {
> - ? ? ? ? record_beneath_to_wait = t->to_wait;
> - ? ? ? ? record_beneath_to_wait_ops = t;
> + ? ? ? ? tmp_to_wait = t->to_wait;
> + ? ? ? ? tmp_to_wait_ops = t;
> ? ? ? ? }
> - ? ? ?if (!record_beneath_to_store_registers)
> + ? ? ?if (!tmp_to_store_registers)
> ? ? ? ? {
> - ? ? ? ? record_beneath_to_store_registers = t->to_store_registers;
> - ? ? ? ? record_beneath_to_store_registers_ops = t;
> + ? ? ? ? tmp_to_store_registers = t->to_store_registers;
> + ? ? ? ? tmp_to_store_registers_ops = t;
> ? ? ? ? }
> - ? ? ?if (!record_beneath_to_xfer_partial)
> + ? ? ?if (!tmp_to_xfer_partial)
> ? ? ? ? {
> - ? ? ? ? record_beneath_to_xfer_partial = t->to_xfer_partial;
> - ? ? ? ? record_beneath_to_xfer_partial_ops = t;
> + ? ? ? ? tmp_to_xfer_partial = t->to_xfer_partial;
> + ? ? ? ? tmp_to_xfer_partial_ops = t;
> ? ? ? ? }
> - ? ? ?if (!record_beneath_to_insert_breakpoint)
> - ? ? ? 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 (!tmp_to_insert_breakpoint)
> + ? ? ? tmp_to_insert_breakpoint = t->to_insert_breakpoint;
> + ? ? ?if (!tmp_to_remove_breakpoint)
> + ? ? ? tmp_to_remove_breakpoint = t->to_remove_breakpoint;
> ? ? }
> - ?if (!record_beneath_to_resume)
> - ? ?error (_("Process record can't get to_resume."));
> - ?if (!record_beneath_to_wait)
> - ? ?error (_("Process record can't get to_wait."));
> - ?if (!record_beneath_to_store_registers)
> - ? ?error (_("Process record can't get to_store_registers."));
> - ?if (!record_beneath_to_xfer_partial)
> + ?if (!tmp_to_xfer_partial)
> ? ? error (_("Process record can't get to_xfer_partial."));
> - ?if (!record_beneath_to_insert_breakpoint)
> - ? ?error (_("Process record can't get to_insert_breakpoint."));
> - ?if (!record_beneath_to_remove_breakpoint)
> - ? ?error (_("Process record can't get to_remove_breakpoint."));
>
> - ?push_target (&record_ops);
> + ?if (current_target.to_stratum == core_stratum)
> + ? ?record_core_open_1 (name, from_tty);
> + ?else
> + ? ?record_open_1 (name, from_tty);
>
> ? /* Reset */
> ? record_insn_num = 0;
> ? record_list = &record_first;
> ? record_list->next = NULL;
> +
> + ?/* Set the tmp beneath pointers to beneath pointers. ?*/
> + ?record_beneath_to_resume_ops = tmp_to_resume_ops;
> + ?record_beneath_to_resume = tmp_to_resume;
> + ?record_beneath_to_wait_ops = tmp_to_wait_ops;
> + ?record_beneath_to_wait = tmp_to_wait;
> + ?record_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
> + ?record_beneath_to_store_registers = tmp_to_store_registers;
> + ?record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops;
> + ?record_beneath_to_xfer_partial = tmp_to_xfer_partial;
> + ?record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
> + ?record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
> +
> + ?/* Load if there is argument. ?*/
> + ?record_load (name);
> ?}
>
> ?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. ?*/
> + ?if (record_core_regbuf)
> + ? ?{
> + ? ? ?xfree (record_core_regbuf);
> + ? ? ?record_core_regbuf = NULL;
> + ? ?}
> +
> + ?/* 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;
> @@ -584,7 +956,7 @@ record_wait (struct target_ops *ops,
> ? ? ? ? ? ? ? ? ? ? ? ?"record_resume_step = %d\n",
> ? ? ? ? ? ? ? ? ? ? ? ?record_resume_step);
>
> - ?if (!RECORD_IS_REPLAY)
> + ?if (!RECORD_IS_REPLAY && ops != &record_core_ops)
> ? ? {
> ? ? ? if (record_resume_error)
> ? ? ? ?{
> @@ -712,76 +1084,10 @@ record_wait (struct target_ops *ops,
> ? ? ? ? ? ? ?break;
> ? ? ? ? ? ?}
>
> - ? ? ? ? /* Set ptid, register and memory according to record_list. ?*/
> - ? ? ? ? if (record_list->type == record_reg)
> - ? ? ? ? ? {
> - ? ? ? ? ? ? /* reg */
> - ? ? ? ? ? ? gdb_byte reg[MAX_REGISTER_SIZE];
> - ? ? ? ? ? ? if (record_debug > 1)
> - ? ? ? ? ? ? ? fprintf_unfiltered (gdb_stdlog,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Process record: record_reg %s to "
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "inferior num = %d.\n",
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? host_address_to_string (record_list),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.reg.num);
> - ? ? ? ? ? ? regcache_cooked_read (regcache, record_list->u.reg.num, reg);
> - ? ? ? ? ? ? regcache_cooked_write (regcache, record_list->u.reg.num,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.reg.val);
> - ? ? ? ? ? ? memcpy (record_list->u.reg.val, reg, MAX_REGISTER_SIZE);
> - ? ? ? ? ? }
> - ? ? ? ? else if (record_list->type == record_mem)
> - ? ? ? ? ? {
> - ? ? ? ? ? ? /* mem */
> - ? ? ? ? ? ? /* Nothing to do if the entry is flagged not_accessible. ?*/
> - ? ? ? ? ? ? if (!record_list->u.mem.mem_entry_not_accessible)
> - ? ? ? ? ? ? ? {
> - ? ? ? ? ? ? ? ? gdb_byte *mem = alloca (record_list->u.mem.len);
> - ? ? ? ? ? ? ? ? if (record_debug > 1)
> - ? ? ? ? ? ? ? ? ? fprintf_unfiltered (gdb_stdlog,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Process record: record_mem %s to "
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "inferior addr = %s len = %d.\n",
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? host_address_to_string (record_list),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? paddress (gdbarch,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.addr),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.len);
> + ? ? ? ? ?record_exec_entry (regcache, gdbarch, record_list,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? (ops == &record_core_ops) ? 1 : 0);
>
> - ? ? ? ? ? ? ? ? if (target_read_memory (record_list->u.mem.addr, mem,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.len))
> - ? ? ? ? ? ? ? ? ? {
> - ? ? ? ? ? ? ? ? ? ? if (execution_direction != EXEC_REVERSE)
> - ? ? ? ? ? ? ? ? ? ? ? error (_("Process record: error reading memory at "
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"addr = %s len = %d."),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?paddress (gdbarch, record_list->u.mem.addr),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.len);
> - ? ? ? ? ? ? ? ? ? ? else
> - ? ? ? ? ? ? ? ? ? ? ? /* Read failed --
> - ? ? ? ? ? ? ? ? ? ? ? ? ?flag entry as not_accessible. ?*/
> - ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.mem_entry_not_accessible = 1;
> - ? ? ? ? ? ? ? ? ? }
> - ? ? ? ? ? ? ? ? else
> - ? ? ? ? ? ? ? ? ? {
> - ? ? ? ? ? ? ? ? ? ? if (target_write_memory (record_list->u.mem.addr,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.val,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.len))
> - ? ? ? ? ? ? ? ? ? ? ? {
> - ? ? ? ? ? ? ? ? ? ? ? ? if (execution_direction != EXEC_REVERSE)
> - ? ? ? ? ? ? ? ? ? ? ? ? ? error (_("Process record: error writing memory at "
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"addr = %s len = %d."),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?paddress (gdbarch, record_list->u.mem.addr),
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.len);
> - ? ? ? ? ? ? ? ? ? ? ? ? else
> - ? ? ? ? ? ? ? ? ? ? ? ? ? /* Write failed --
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?flag entry as not_accessible. ?*/
> - ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.mem_entry_not_accessible = 1;
> - ? ? ? ? ? ? ? ? ? ? ? }
> - ? ? ? ? ? ? ? ? ? ? else
> - ? ? ? ? ? ? ? ? ? ? ? {
> - ? ? ? ? ? ? ? ? ? ? ? ? memcpy (record_list->u.mem.val, mem,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? record_list->u.mem.len);
> - ? ? ? ? ? ? ? ? ? ? ? }
> - ? ? ? ? ? ? ? ? ? }
> - ? ? ? ? ? ? ? }
> - ? ? ? ? ? }
> - ? ? ? ? else
> + ? ? ? ? if (record_list->type == record_end)
> ? ? ? ? ? ?{
> ? ? ? ? ? ? ?if (record_debug > 1)
> ? ? ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog,
> @@ -901,6 +1207,7 @@ record_kill (struct target_ops *ops)
> ? ? fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
>
> ? unpush_target (&record_ops);
> +
> ? target_kill ();
> ?}
>
> @@ -945,7 +1252,7 @@ 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++;
> ?}
> @@ -1058,7 +1365,7 @@ 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++;
> ? ? }
> @@ -1138,6 +1445,191 @@ init_record_ops (void)
> ?}
>
> ?static void
> +record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
> + ? ? ? ? ? ? ? ? ? ?enum target_signal siggnal)
> +{
> + ?record_resume_step = step;
> + ?record_resume_siggnal = siggnal;
> +}
> +
> +static void
> +record_core_kill (struct target_ops *ops)
> +{
> + ?if (record_debug)
> + ? ?fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
> +
> + ?unpush_target (&record_core_ops);
> +}
> +
> +static void
> +record_core_fetch_registers (struct target_ops *ops,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct regcache *regcache,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? int regno)
> +{
> + ?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);
> +}
> +
> +static void
> +record_core_prepare_to_store (struct regcache *regcache)
> +{
> +}
> +
> +static void
> +record_core_store_registers (struct target_ops *ops,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct regcache *regcache,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? int regno)
> +{
> + ?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."));
> +}
> +
> +static LONGEST
> +record_core_xfer_partial (struct target_ops *ops, enum target_object object,
> + ? ? ? ? ? ? ? ? ? ? ? ? const char *annex, gdb_byte *readbuf,
> + ? ? ? ? ? ? ? ? ? ? ? ? const gdb_byte *writebuf, ULONGEST offset,
> + ? ? ? ? ? ? ? ? ? ? ? ? ?LONGEST len)
> +{
> + ? if (object == TARGET_OBJECT_MEMORY)
> + ? ? {
> + ? ? ? 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 -1;
> + ? ? ? ? }
> + ? ? ? else
> + ? ? ? ? error (_("You can't do that without a process to debug."));
> + ? ? }
> +
> + ?return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? object, annex, readbuf, writebuf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? offset, len);
> +}
> +
> +static int
> +record_core_insert_breakpoint (struct gdbarch *gdbarch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct bp_target_info *bp_tgt)
> +{
> + ?return 0;
> +}
> +
> +static int
> +record_core_remove_breakpoint (struct gdbarch *gdbarch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct bp_target_info *bp_tgt)
> +{
> + ?return 0;
> +}
> +
> +int
> +record_core_has_execution (struct target_ops *ops)
> +{
> + ?return 1;
> +}
> +
> +static void
> +init_record_core_ops (void)
> +{
> + ?record_core_ops.to_shortname = "record_core";
> + ?record_core_ops.to_longname = "Process record and replay target";
> + ?record_core_ops.to_doc =
> + ? ?"Log program while executing and replay execution from log.";
> + ?record_core_ops.to_open = record_open;
> + ?record_core_ops.to_close = record_close;
> + ?record_core_ops.to_resume = record_core_resume;
> + ?record_core_ops.to_wait = record_wait;
> + ?record_core_ops.to_kill = record_core_kill;
> + ?record_core_ops.to_fetch_registers = record_core_fetch_registers;
> + ?record_core_ops.to_prepare_to_store = record_core_prepare_to_store;
> + ?record_core_ops.to_store_registers = record_core_store_registers;
> + ?record_core_ops.to_xfer_partial = record_core_xfer_partial;
> + ?record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
> + ?record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
> + ?record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
> + ?record_core_ops.to_has_execution = record_core_has_execution;
> + ?record_core_ops.to_stratum = record_stratum;
> + ?record_core_ops.to_magic = OPS_MAGIC;
> +}
> +
> +static void
> ?show_record_debug (struct ui_file *file, int from_tty,
> ? ? ? ? ? ? ? ? ? struct cmd_list_element *c, const char *value)
> ?{
> @@ -1153,6 +1645,212 @@ cmd_record_start (char *args, int from_t
> ? execute_command ("target record", from_tty);
> ?}
>
> +static void
> +cmd_record_load (char *args, int from_tty)
> +{
> + ?char buf[512];
> +
> + ?snprintf (buf, 512, "target record %s", args);
> + ?execute_command (buf, from_tty);
> +}
> +
> +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);
> +}
> +
> +/* Record log save-file format
> + ? Version 1
> +
> + ? Header:
> + ? ? 4 bytes: magic number htonl(0x20090726).
> + ? ? ? 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 (network byte order).
> + ? ? ? MAX_REGISTER_SIZE bytes: register value.
> + ? ? record_mem:
> + ? ? ? 1 byte: ?record type (record_mem).
> + ? ? ? 8 bytes: memory address (network byte order).
> + ? ? ? 8 bytes: memory length (network byte order).
> + ? ? ? n bytes: memory value (n == memory length).
> +*/
> +
> +/* 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 (strcmp (current_target.to_shortname, "record") != 0)
> + ? ?error (_("Process record is not started.\n"));
> +
> + ?if (args && *args)
> + ? ?recfilename = args;
> + ?else
> + ? ?{
> + ? ? ?/* Default corefile name is "gdb_record.PID". ?*/
> + ? ? ?snprintf (recfilename_buffer, 40, "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 (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, 0);
> +
> + ? ? ?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 */
> + ? ? ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog, _("\
> +Writing register %d (1 plus 8 plus %d bytes)\n"),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.reg.num,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?MAX_REGISTER_SIZE);
> +
> + ? ? ? ? ? ? ?tmpu64 = record_list->u.reg.num;
> + ? ? ? ? ? ? ?if (BYTE_ORDER == LITTLE_ENDIAN)
> + ? ? ? ? ? ? ? ?tmpu64 = bswap_64 (tmpu64);
> + ? ? ? ? ? ? ?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)
> + ? ? ? ? ? ? ? ?{
> + ? ? ? ? ? ? ? ? ?if (record_debug)
> + ? ? ? ? ? ? ? ? ? ?fprintf_unfiltered (gdb_stdlog, _("\
> +Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?paddress (gdbarch,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.addr),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?record_list->u.mem.len);
> +
> + ? ? ? ? ? ? ? ? ?tmpu64 = record_list->u.mem.addr;
> + ? ? ? ? ? ? ? ? ?if (BYTE_ORDER == LITTLE_ENDIAN)
> + ? ? ? ? ? ? ? ? ? ?tmpu64 = bswap_64 (tmpu64);
> + ? ? ? ? ? ? ? ? ?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, 0);
> +
> + ? ? ?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, 0);
> +
> + ? ? ?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);
> +}
> +
> ?/* Truncate the record log from the present point
> ? ?of replay until the end. ?*/
>
> @@ -1185,7 +1883,12 @@ cmd_record_stop (char *args, int from_tt
> ? ? {
> ? ? ? if (!record_list || !from_tty || query (_("Delete recorded log and "
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"stop recording?")))
> - ? ? ? unpush_target (&record_ops);
> + ? ? ? ?{
> + ? ? ? ? ?if (strcmp (current_target.to_shortname, "record") == 0)
> + ? ? ? ? ? unpush_target (&record_ops);
> + ? ? ? ? ?else
> + ? ? ? ? ? ?unpush_target (&record_core_ops);
> + ? ? ? ?}
> ? ? }
> ? else
> ? ? printf_unfiltered (_("Process record is not started.\n"));
> @@ -1203,7 +1906,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 ();
> ? ? }
> ?}
>
> @@ -1243,6 +1946,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;
> @@ -1250,6 +1955,8 @@ _initialize_record (void)
>
> ? init_record_ops ();
> ? add_target (&record_ops);
> + ?init_record_core_ops ();
> + ?add_target (&record_core_ops);
>
> ? add_setshow_zinteger_cmd ("record", no_class, &record_debug,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ?_("Set debugging of record/replay feature."),
> @@ -1259,9 +1966,10 @@ _initialize_record (void)
> ? ? ? ? ? ? ? ? ? ? ? ? ? ?NULL, show_record_debug, &setdebuglist,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ?&showdebuglist);
>
> - ?add_prefix_cmd ("record", class_obscure, cmd_record_start,
> - ? ? ? ? ? ? ? ? _("Abbreviated form of \"target record\" command."),
> - ? ? ? ? ? ? ? ? &record_cmdlist, "record ", 0, &cmdlist);
> + ?c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
> + ? ? ? ? ? ? ? ? ? ? _("Abbreviated form of \"target record\" command."),
> + ? ? ? ? ? ? ? ? ? ? &record_cmdlist, "record ", 0, &cmdlist);
> + ?set_cmd_completer (c, filename_completer);
> ? add_com_alias ("rec", "record", class_obscure, 1);
> ? add_prefix_cmd ("record", class_support, set_record_command,
> ? ? ? ? ? ? ? ? ?_("Set record options"), &set_record_cmdlist,
> @@ -1276,6 +1984,16 @@ _initialize_record (void)
> ? ? ? ? ? ? ? ? ?"info record ", 0, &infolist);
> ? add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
>
> + ?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);
> + ?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);
>
> ? add_cmd ("delete", class_obscure, cmd_record_delete,
> ? ? ? ? ? _("Delete the rest of execution log and start recording it anew."),
>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]