Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFA, 2 of 3] save/restore process record, part 2 (core ops target)
@ 2009-10-17  1:30 Michael Snyder
  2009-10-17  8:07 ` Eli Zaretskii
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Snyder @ 2009-10-17  1:30 UTC (permalink / raw)
  To: gdb-patches, Hui Zhu

[-- Attachment #1: Type: text/plain, Size: 349 bytes --]

This is part 2 of the sync-up for process record save/restore,
split into parts for easier review.

This patch introduces a core-ops target to record.c, so that
we can use the core target as a target-beneath.  We'll do that
when we restore a saved execution log (patch part 3), because
we use core files as the underlying layer of our save format.


[-- Attachment #2: tmp2.txt --]
[-- Type: text/plain, Size: 17000 bytes --]

2009-10-16  Hui Zhu  <teawater@gmail.com>
	    Michael Snyder  <msnyder@vmware.com>

	* record.c (record_core_ops): New ops target vector.
	(record_core_open_1): New function.  Open core target.
	(record_open_1): New function.  Open normal process record.
	(record_open): Call one of the above.
	(record_close): Allow for both core and normal targets.
	(record_core_resume): New function.
	(record_core_kill): New function.
	(record_core_fetch_registers): New function.
	(record_core_prepare_to_store): New function.
	(record_core_store_registers): New function.
	(record_core_xfer_partial): New function.
	(record_core_insert_breakpoint): New function.
	(record_core_remove_breakpoint): New function.
	(record_core_has_execution): New function.
	(init_record_core_ops): New function.
	(_initialize_record): Call init_record_core_ops, add_target.

--- record.1.c	2009-10-16 14:32:51.000000000 -0700
+++ record.2.c	2009-10-16 15:09:49.000000000 -0700
@@ -23,6 +23,8 @@
 #include "gdbthread.h"
 #include "event-top.h"
 #include "exceptions.h"
+#include "gdbcore.h"
+#include "exec.h"
 #include "record.h"
 
 #include <signal.h>
@@ -100,6 +102,19 @@ struct record_entry
 /* This is the debug switch for process record.  */
 int record_debug = 0;
 
+struct record_core_buf_entry
+{
+  struct record_core_buf_entry *prev;
+  struct target_section *p;
+  bfd_byte *buf;
+};
+
+/* 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;
@@ -114,6 +129,7 @@ static unsigned long long record_insn_co
 
 /* 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;
@@ -588,6 +604,8 @@ record_gdb_operation_disable_set (void)
   return old_cleanups;
 }
 
+/* Execute one instruction from the record log.  */
+
 static inline void
 record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch,
                    struct record_entry *entry)
@@ -658,8 +676,60 @@ record_exec_entry (struct regcache *regc
     }
 }
 
+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 *);
+
+/* Open the process record target.  */
+
 static void
-record_open (char *name, int from_tty)
+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;
+
+  /* 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;
 
@@ -680,76 +750,130 @@ 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)
     error (_("Process record target already running.  Use \"record stop\" to "
              "stop record target first."));
 
-  /*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_insn_count = 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;
+
+  //if (strcmp (current_target.to_shortname, "record_core") == 0)
+  //record_load ();
 }
 
 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;
@@ -821,7 +945,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)
 	{
@@ -1320,6 +1444,190 @@ init_record_ops (void)
 }
 
 static void
+record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
+                    enum target_signal signal)
+{
+  record_resume_step = step;
+}
+
+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)
 {
@@ -1449,6 +1757,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."),

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops target)
  2009-10-17  1:30 [RFA, 2 of 3] save/restore process record, part 2 (core ops target) Michael Snyder
@ 2009-10-17  8:07 ` Eli Zaretskii
  2009-10-17 17:14   ` Michael Snyder
  0 siblings, 1 reply; 7+ messages in thread
From: Eli Zaretskii @ 2009-10-17  8:07 UTC (permalink / raw)
  To: Michael Snyder; +Cc: gdb-patches, teawater

> Date: Fri, 16 Oct 2009 18:24:43 -0700
> From: Michael Snyder <msnyder@vmware.com>
> 
> +  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."));

Can we rephrase these to be more user-friendly?  As written, this text
is okay for debug printouts, but not for user-level error messages,
IMO.  (Yes, I know this text was in the old version, but still...)

> +  //if (strcmp (current_target.to_shortname, "record_core") == 0)
> +  //record_load ();

C++-style comments?

Thanks.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops target)
  2009-10-17  8:07 ` Eli Zaretskii
@ 2009-10-17 17:14   ` Michael Snyder
  2009-10-17 19:56     ` Eli Zaretskii
  2009-10-18  7:26     ` Hui Zhu
  0 siblings, 2 replies; 7+ messages in thread
From: Michael Snyder @ 2009-10-17 17:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, teawater

[-- Attachment #1: Type: text/plain, Size: 1217 bytes --]

Eli Zaretskii wrote:
>> Date: Fri, 16 Oct 2009 18:24:43 -0700
>> From: Michael Snyder <msnyder@vmware.com>
>>
>> +  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."));
> 
> Can we rephrase these to be more user-friendly?  As written, this text
> is okay for debug printouts, but not for user-level error messages,
> IMO.  (Yes, I know this text was in the old version, but still...)

No problem, see revised diff (attached).  However, in the unlikely
event that these occur, there won't be anything that the user can
do about it.


>> +  //if (strcmp (current_target.to_shortname, "record_core") == 0)
>> +  //record_load ();
> 
> C++-style comments?

It is just to comment out a reference to a function that will not
be present until the third patch.  I'll take it out.

Thanks for your review.


[-- Attachment #2: tmp2b.txt --]
[-- Type: text/plain, Size: 16722 bytes --]

2009-10-16  Hui Zhu  <teawater@gmail.com>
	    Michael Snyder  <msnyder@vmware.com>

	* record.c (record_core_ops): New ops target vector.
	(record_core_open_1): New function.  Open core target.
	(record_open_1): New function.  Open normal process record.
	(record_open): Call one of the above.
	(record_close): Allow for both core and normal targets.
	(record_core_resume): New function.
	(record_core_kill): New function.
	(record_core_fetch_registers): New function.
	(record_core_prepare_to_store): New function.
	(record_core_store_registers): New function.
	(record_core_xfer_partial): New function.
	(record_core_insert_breakpoint): New function.
	(record_core_remove_breakpoint): New function.
	(record_core_has_execution): New function.
	(init_record_core_ops): New function.
	(_initialize_record): Call init_record_core_ops, add_target.

--- record.1b.c	2009-10-17 09:55:13.000000000 -0700
+++ record.2.c	2009-10-17 10:03:58.000000000 -0700
@@ -23,6 +23,8 @@
 #include "gdbthread.h"
 #include "event-top.h"
 #include "exceptions.h"
+#include "gdbcore.h"
+#include "exec.h"
 #include "record.h"
 
 #include <signal.h>
@@ -100,6 +102,19 @@ struct record_entry
 /* This is the debug switch for process record.  */
 int record_debug = 0;
 
+struct record_core_buf_entry
+{
+  struct record_core_buf_entry *prev;
+  struct target_section *p;
+  bfd_byte *buf;
+};
+
+/* 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;
@@ -114,6 +129,7 @@ static unsigned long long record_insn_co
 
 /* 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;
@@ -662,8 +678,60 @@ record_exec_insn (struct regcache *regca
     }
 }
 
+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 *);
+
+/* Open the process record target.  */
+
+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;
+
+  /* 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 (char *name, int from_tty)
+record_open_1 (char *name, int from_tty)
 {
   struct target_ops *t;
 
@@ -684,76 +752,127 @@ record_open (char *name, int from_tty)
     error (_("Process record: the current architecture doesn't support "
 	     "record function."));
 
+  if (!tmp_to_resume)
+    error (_("Could not find 'to_resume' method on the target stack."));
+  if (!tmp_to_wait)
+    error (_("Could not find 'to_wait' method on the target stack."));
+  if (!tmp_to_store_registers)
+    error (_("Could not find 'to_store_registers' method on the target stack."));
+  if (!tmp_to_insert_breakpoint)
+    error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
+  if (!tmp_to_remove_breakpoint)
+    error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
+
+  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)
     error (_("Process record target already running.  Use \"record stop\" to "
              "stop record target first."));
 
-  /*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_insn_count = 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;
 }
 
 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;
@@ -825,7 +944,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)
 	{
@@ -1324,6 +1443,190 @@ init_record_ops (void)
 }
 
 static void
+record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
+                    enum target_signal signal)
+{
+  record_resume_step = step;
+}
+
+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)
 {
@@ -1453,6 +1756,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."),

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops target)
  2009-10-17 17:14   ` Michael Snyder
@ 2009-10-17 19:56     ` Eli Zaretskii
  2009-10-22 19:43       ` Michael Snyder
  2009-10-18  7:26     ` Hui Zhu
  1 sibling, 1 reply; 7+ messages in thread
From: Eli Zaretskii @ 2009-10-17 19:56 UTC (permalink / raw)
  To: Michael Snyder; +Cc: gdb-patches, teawater

> Date: Sat, 17 Oct 2009 10:08:50 -0700
> From: Michael Snyder <msnyder@vmware.com>
> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>, 
>  "teawater@gmail.com" <teawater@gmail.com>
> 
> This is a multi-part message in MIME format.
> --------------040403070509020904040303
> Content-Type: text/plain; charset=ISO-8859-1; format=flowed
> Content-Transfer-Encoding: 7bit
> 
> Eli Zaretskii wrote:
> >> Date: Fri, 16 Oct 2009 18:24:43 -0700
> >> From: Michael Snyder <msnyder@vmware.com>
> >>
> >> +  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."));
> > 
> > Can we rephrase these to be more user-friendly?  As written, this text
> > is okay for debug printouts, but not for user-level error messages,
> > IMO.  (Yes, I know this text was in the old version, but still...)
> 
> No problem, see revised diff (attached).  However, in the unlikely
> event that these occur, there won't be anything that the user can
> do about it.

Thanks, the revised text is fine with me.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops   target)
  2009-10-17 17:14   ` Michael Snyder
  2009-10-17 19:56     ` Eli Zaretskii
@ 2009-10-18  7:26     ` Hui Zhu
  2009-10-18 17:31       ` Michael Snyder
  1 sibling, 1 reply; 7+ messages in thread
From: Hui Zhu @ 2009-10-18  7:26 UTC (permalink / raw)
  To: Michael Snyder; +Cc: Eli Zaretskii, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 20667 bytes --]

patch -p0 < ~/Desktop/tmp2b.diff
patching file record.c
Hunk #2 succeeded at 101 (offset -1 lines).
Hunk #3 succeeded at 127 (offset -2 lines).
Hunk #4 succeeded at 675 (offset -3 lines).
Hunk #5 FAILED at 749.
Hunk #6 succeeded at 940 (offset -4 lines).
Hunk #7 succeeded at 1439 (offset -4 lines).
Hunk #8 succeeded at 1735 (offset -21 lines).
1 out of 8 hunks FAILED -- saving rejects to file record.c.rej




On Sun, Oct 18, 2009 at 01:08, Michael Snyder <msnyder@vmware.com> wrote:
> Eli Zaretskii wrote:
>>>
>>> Date: Fri, 16 Oct 2009 18:24:43 -0700
>>> From: Michael Snyder <msnyder@vmware.com>
>>>
>>> +  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."));
>>
>> Can we rephrase these to be more user-friendly?  As written, this text
>> is okay for debug printouts, but not for user-level error messages,
>> IMO.  (Yes, I know this text was in the old version, but still...)
>
> No problem, see revised diff (attached).  However, in the unlikely
> event that these occur, there won't be anything that the user can
> do about it.
>
>
>>> +  //if (strcmp (current_target.to_shortname, "record_core") == 0)
>>> +  //record_load ();
>>
>> C++-style comments?
>
> It is just to comment out a reference to a function that will not
> be present until the third patch.  I'll take it out.
>
> Thanks for your review.
>
>
> 2009-10-16  Hui Zhu  <teawater@gmail.com>
>            Michael Snyder  <msnyder@vmware.com>
>
>        * record.c (record_core_ops): New ops target vector.
>        (record_core_open_1): New function.  Open core target.
>        (record_open_1): New function.  Open normal process record.
>        (record_open): Call one of the above.
>        (record_close): Allow for both core and normal targets.
>        (record_core_resume): New function.
>        (record_core_kill): New function.
>        (record_core_fetch_registers): New function.
>        (record_core_prepare_to_store): New function.
>        (record_core_store_registers): New function.
>        (record_core_xfer_partial): New function.
>        (record_core_insert_breakpoint): New function.
>        (record_core_remove_breakpoint): New function.
>        (record_core_has_execution): New function.
>        (init_record_core_ops): New function.
>        (_initialize_record): Call init_record_core_ops, add_target.
>
> --- record.1b.c 2009-10-17 09:55:13.000000000 -0700
> +++ record.2.c  2009-10-17 10:03:58.000000000 -0700
> @@ -23,6 +23,8 @@
>  #include "gdbthread.h"
>  #include "event-top.h"
>  #include "exceptions.h"
> +#include "gdbcore.h"
> +#include "exec.h"
>  #include "record.h"
>
>  #include <signal.h>
> @@ -100,6 +102,19 @@ struct record_entry
>  /* This is the debug switch for process record.  */
>  int record_debug = 0;
>
> +struct record_core_buf_entry
> +{
> +  struct record_core_buf_entry *prev;
> +  struct target_section *p;
> +  bfd_byte *buf;
> +};
> +
> +/* 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;
> @@ -114,6 +129,7 @@ static unsigned long long record_insn_co
>
>  /* 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;
> @@ -662,8 +678,60 @@ record_exec_insn (struct regcache *regca
>     }
>  }
>
> +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 *);
> +
> +/* Open the process record target.  */
> +
> +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;
> +
> +  /* 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 (char *name, int from_tty)
> +record_open_1 (char *name, int from_tty)
>  {
>   struct target_ops *t;
>
> @@ -684,76 +752,127 @@ record_open (char *name, int from_tty)
>     error (_("Process record: the current architecture doesn't support "
>             "record function."));
>
> +  if (!tmp_to_resume)
> +    error (_("Could not find 'to_resume' method on the target stack."));
> +  if (!tmp_to_wait)
> +    error (_("Could not find 'to_wait' method on the target stack."));
> +  if (!tmp_to_store_registers)
> +    error (_("Could not find 'to_store_registers' method on the target
> stack."));
> +  if (!tmp_to_insert_breakpoint)
> +    error (_("Could not find 'to_insert_breakpoint' method on the target
> stack."));
> +  if (!tmp_to_remove_breakpoint)
> +    error (_("Could not find 'to_remove_breakpoint' method on the target
> stack."));
> +
> +  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)
>     error (_("Process record target already running.  Use \"record stop\" to
> "
>              "stop record target first."));
>
> -  /*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_insn_count = 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;
>  }
>
>  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;
> @@ -825,7 +944,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)
>        {
> @@ -1324,6 +1443,190 @@ init_record_ops (void)
>  }
>
>  static void
> +record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
> +                    enum target_signal signal)
> +{
> +  record_resume_step = step;
> +}
> +
> +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)
>  {
> @@ -1453,6 +1756,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."),
>
>

[-- Attachment #2: record.c.rej --]
[-- Type: application/octet-stream, Size: 6826 bytes --]

*************** record_open (char *name, int from_tty)
*** 681,756 ****
      error (_("Process record: the current architecture doesn't support "
  	     "record function."));
  
    /* Check if record target is already running.  */
    if (current_target.to_stratum == record_stratum)
      error (_("Process record target already running.  Use \"record stop\" to "
               "stop record target first."));
  
-   /*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;
  
    /* Set the beneath function pointers.  */
    for (t = current_target.beneath; t != NULL; t = t->beneath)
      {
-       if (!record_beneath_to_resume)
          {
- 	  record_beneath_to_resume = t->to_resume;
- 	  record_beneath_to_resume_ops = t;
          }
-       if (!record_beneath_to_wait)
          {
- 	  record_beneath_to_wait = t->to_wait;
- 	  record_beneath_to_wait_ops = t;
          }
-       if (!record_beneath_to_store_registers)
          {
- 	  record_beneath_to_store_registers = t->to_store_registers;
- 	  record_beneath_to_store_registers_ops = t;
          }
-       if (!record_beneath_to_xfer_partial)
          {
- 	  record_beneath_to_xfer_partial = t->to_xfer_partial;
- 	  record_beneath_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 (!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)
      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);
  
    /* Reset */
    record_insn_num = 0;
    record_insn_count = 0;
    record_list = &record_first;
    record_list->next = NULL;
  }
  
  static void
  record_close (int quitting)
  {
    if (record_debug)
      fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
  
    record_list_release (record_list);
  }
  
  static int record_resume_step = 0;
--- 749,875 ----
      error (_("Process record: the current architecture doesn't support "
  	     "record function."));
  
+   if (!tmp_to_resume)
+     error (_("Could not find 'to_resume' method on the target stack."));
+   if (!tmp_to_wait)
+     error (_("Could not find 'to_wait' method on the target stack."));
+   if (!tmp_to_store_registers)
+     error (_("Could not find 'to_store_registers' method on the target stack."));
+   if (!tmp_to_insert_breakpoint)
+     error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
+   if (!tmp_to_remove_breakpoint)
+     error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
+ 
+   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)
      error (_("Process record target already running.  Use \"record stop\" to "
               "stop record target first."));
  
+   /* 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 (!tmp_to_resume)
          {
+ 	  tmp_to_resume = t->to_resume;
+ 	  tmp_to_resume_ops = t;
          }
+       if (!tmp_to_wait)
          {
+ 	  tmp_to_wait = t->to_wait;
+ 	  tmp_to_wait_ops = t;
          }
+       if (!tmp_to_store_registers)
          {
+ 	  tmp_to_store_registers = t->to_store_registers;
+ 	  tmp_to_store_registers_ops = t;
          }
+       if (!tmp_to_xfer_partial)
          {
+ 	  tmp_to_xfer_partial = t->to_xfer_partial;
+ 	  tmp_to_xfer_partial_ops = t;
          }
+       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 (!tmp_to_xfer_partial)
      error (_("Process record can't get to_xfer_partial."));
  
+   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_insn_count = 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;
  }
  
  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;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops   target)
  2009-10-18  7:26     ` Hui Zhu
@ 2009-10-18 17:31       ` Michael Snyder
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Snyder @ 2009-10-18 17:31 UTC (permalink / raw)
  To: Hui Zhu; +Cc: gdb-patches

Hui Zhu wrote:
> patch -p0 < ~/Desktop/tmp2b.diff
> patching file record.c
> Hunk #2 succeeded at 101 (offset -1 lines).
> Hunk #3 succeeded at 127 (offset -2 lines).
> Hunk #4 succeeded at 675 (offset -3 lines).
> Hunk #5 FAILED at 749.
> Hunk #6 succeeded at 940 (offset -4 lines).
> Hunk #7 succeeded at 1439 (offset -4 lines).
> Hunk #8 succeeded at 1735 (offset -21 lines).
> 1 out of 8 hunks FAILED -- saving rejects to file record.c.rej

Hmmm, doesn't fail for me -- and no offsets either.
Are you sure you applied tmp1b.txt first?
And did you apply them on top of savemem.txt?


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFA, 2 of 3] save/restore process record, part 2 (core ops target)
  2009-10-17 19:56     ` Eli Zaretskii
@ 2009-10-22 19:43       ` Michael Snyder
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Snyder @ 2009-10-22 19:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, teawater

Eli Zaretskii wrote:
>> Date: Sat, 17 Oct 2009 10:08:50 -0700
>> From: Michael Snyder <msnyder@vmware.com>
>> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>, 
>>  "teawater@gmail.com" <teawater@gmail.com>
>>
>> This is a multi-part message in MIME format.
>> --------------040403070509020904040303
>> Content-Type: text/plain; charset=ISO-8859-1; format=flowed
>> Content-Transfer-Encoding: 7bit
>>
>> Eli Zaretskii wrote:
>>>> Date: Fri, 16 Oct 2009 18:24:43 -0700
>>>> From: Michael Snyder <msnyder@vmware.com>
>>>>
>>>> +  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."));
>>> Can we rephrase these to be more user-friendly?  As written, this text
>>> is okay for debug printouts, but not for user-level error messages,
>>> IMO.  (Yes, I know this text was in the old version, but still...)
>> No problem, see revised diff (attached).  However, in the unlikely
>> event that these occur, there won't be anything that the user can
>> do about it.
> 
> Thanks, the revised text is fine with me.
> 

And committed.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-10-22 19:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-17  1:30 [RFA, 2 of 3] save/restore process record, part 2 (core ops target) Michael Snyder
2009-10-17  8:07 ` Eli Zaretskii
2009-10-17 17:14   ` Michael Snyder
2009-10-17 19:56     ` Eli Zaretskii
2009-10-22 19:43       ` Michael Snyder
2009-10-18  7:26     ` Hui Zhu
2009-10-18 17:31       ` Michael Snyder

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox