From: Hui Zhu <teawater@gmail.com>
To: Michael Snyder <msnyder@vmware.com>
Cc: Eli Zaretskii <eliz@gnu.org>,
"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: Re: [RFA/RFC] Add dump and load command to process record and replay
Date: Tue, 25 Aug 2009 08:47:00 -0000 [thread overview]
Message-ID: <daef60380908242353q428e1e04x7912b48b2e37a7c2@mail.gmail.com> (raw)
In-Reply-To: <4A92D317.9010002@vmware.com>
On Tue, Aug 25, 2009 at 01:51, Michael Snyder<msnyder@vmware.com> wrote:
> Hui Zhu wrote:
>>
>> On Mon, Aug 24, 2009 at 07:21, Michael Snyder<msnyder@vmware.com> wrote:
>>>
>>> Hui Zhu wrote:
>>>
>>>> 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.
>>>
>>> Hi Hui,
>>>
>>> In this review, I'm going to comment only on the parts of the
>>> patch that relate to the record_core_ops (ie. pushing the
>>> record stratum on top of the core file stratum).
>>>
>>> Those parts of the patch are much improved. I like this
>>> version a lot better. Thanks for reworking it.
>>>
>>
>>
>> Hi Michael,
>>
>> Thanks for your review. I make a new patch that update the memset code
>> to:
>
> Way cool, thanks!
>
> Now I can comment on the dump/load part of the patch.
> It does not seem as if previous issues have been addressed:
> 1) Core file and record file are separate, no way to verify
> that they correspond to each other.
> 2) Previous log not cleared when loading a new log.
For it, I think we don't need worry about it. Because I make the
record load together with record_open. So each time when we load
record, the record list is empty.
>
> Have you considered combining this patch with the idea that I
> proposed -- storing the record log in the core file? Seems like
> that would be very easy now -- gdb already has the core file open,
> and there is a global pointer to the BFD.
> See gdbcore.h:extern bfd* core_bfd;
Cool. If we just want record_core_ops support load, we can remove the
cmd "record load". :)
>
> In fact, just for fun, I've merged the two patches in my
> local tree. It seems to work quite well. I can just post
> it here if you like, or here's a hint to show you how it goes:
Why not? Please post it. I can not wait to try it. :)
Thanks,
Hui
>
> static void
> record_load (void)
> {
> int recfd;
> uint32_t magic;
> struct cleanup *old_cleanups2;
> struct record_entry *rec;
> int insn_number = 0;
> asection *osec;
> void nullify_last_target_wait_ptid (void);
>
> /* We load the execution log from the open core bfd,
> if there is one. */
> if (core_bfd == NULL)
> return;
>
> if (record_debug)
> fprintf_filtered (gdb_stdlog,
> _("Restoring recording from core file.\n"));
>
> /* Now need to find our special note section. */
> osec = bfd_get_section_by_name (core_bfd, "null0");
>
>
>> if (target_read_memory (entry->u.mem.addr, mem,
>> entry->u.mem.len))
>> {
>> 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
>> {
>> if (target_write_memory (entry->u.mem.addr,
>> entry->u.mem.val,
>> entry->u.mem.len))
>> {
>> 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
>> memcpy (entry->u.mem.val, mem, entry->u.mem.len);
>> }
>>
>> Please help me review it.
>>
>> Thanks,
>> Hui
>>
>> ---
>> record.c | 951
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
>> 1 file changed, 825 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,291 @@ 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)
>> +{
>> + 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))
>> + {
>> + 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
>> + {
>> + if (target_write_memory (entry->u.mem.addr,
>> entry->u.mem.val,
>> + entry->u.mem.len))
>> + {
>> + 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
>> + 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 +738,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 +769,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 +938,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 +1066,9 @@ 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);
>>
>> - 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 +1188,7 @@ record_kill (struct target_ops *ops)
>> fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
>>
>> unpush_target (&record_ops);
>> +
>> target_kill ();
>> }
>>
>> @@ -945,7 +1233,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 +1346,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 +1426,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 +1626,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);
>> +
>> + 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);
>> +
>> + 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);
>> +}
>> +
>> /* Truncate the record log from the present point
>> of replay until the end. */
>>
>> @@ -1185,7 +1864,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 +1887,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 +1927,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 +1936,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 +1947,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 +1965,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."),
>
>
next prev parent reply other threads:[~2009-08-25 6:53 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
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 [this message]
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=daef60380908242353q428e1e04x7912b48b2e37a7c2@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