Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Michael Snyder <msnyder@vmware.com>
To: Hui Zhu <teawater@gmail.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: Mon, 24 Aug 2009 18:32:00 -0000	[thread overview]
Message-ID: <4A92D317.9010002@vmware.com> (raw)
In-Reply-To: <daef60380908240045m1fe6c4f8jc61e10b45c6ab221@mail.gmail.com>

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.

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;

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:

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."),


  reply	other threads:[~2009-08-24 17:56 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 [this message]
2009-08-25  8:47                                                 ` Hui Zhu
2009-08-26  1:40                                                   ` Michael Snyder
2009-08-26  2:59                                                     ` Michael Snyder
2009-08-29 15:53                                                       ` Hui Zhu
2009-08-29 18:06                                                         ` Eli Zaretskii
2009-08-29 18:28                                                           ` Hui Zhu
2009-08-29 20:26                                                             ` Eli Zaretskii
2009-08-29 20:39                                                               ` Michael Snyder
2009-08-30  3:03                                                                 ` Eli Zaretskii
2009-08-30  5:36                                                                   ` Hui Zhu
2009-08-30 23:40                                                                     ` Eli Zaretskii
2009-08-31  7:10                                                                       ` Hui Zhu
2009-09-05  3:30                                                                         ` Michael Snyder
2009-09-06 16:29                                                                           ` Hui Zhu
2009-08-29 20:05                                                           ` Michael Snyder
2009-08-29 20:33                                                             ` Eli Zaretskii
2009-08-29 21:20                                                               ` Michael Snyder
2022-01-21  6:46 Simon Sobisch via Gdb-patches
2022-01-24  9:26 ` Hui Zhu via Gdb-patches
2022-04-13 12:21   ` Simon Sobisch via Gdb-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4A92D317.9010002@vmware.com \
    --to=msnyder@vmware.com \
    --cc=eliz@gnu.org \
    --cc=gdb-patches@sourceware.org \
    --cc=teawater@gmail.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