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


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

Re: [RFA] Resubmit process record and replay, 3/10


teawater wrote:
This patch add the process record and replay target.  This is the core
part of process record and replay.

2008-11-16 Hui Zhu <teawater@gmail.com>

Process record and replay target.

        * Makefile.in (record.c): New file.
        * record.c, record.h: New file.

 Makefile.in |    4
 b/record.c  | 1156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 b/record.h  |   99 +++++
 3 files changed, 1257 insertions(+), 2 deletions(-)

Following is the diff with the previous patch:

I don't think a diff with the previous patch is the way to go here. The makefile part of this diff is not very understandable.

Could you repost this patch with the usual diff
between Makefile.in and the current version that's
in CVS?

Thanks!

@@ -1,6 +1,6 @@
 --- a/Makefile.in
 +++ b/Makefile.in
-@@ -657,7 +657,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
+@@ -657,7 +657,7 @@
        valarith.c valops.c valprint.c value.c varobj.c vec.c \
        wrapper.c \
        xml-tdesc.c xml-support.c \
@@ -9,7 +9,7 @@

LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c

-@@ -808,7 +808,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
+@@ -808,7 +808,7 @@
        solib.o solib-null.o \
        prologue-value.o memory-map.o xml-support.o \
        target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
@@ -18,9 +18,9 @@

TSOBS = inflow.o

---- a/record.c
+--- a//dev/null
 +++ b/record.c
-@@ -0,0 +1,1143 @@
+@@ -0,0 +1,1156 @@
 +/* Process record and replay target for GDB, the GNU debugger.
 +
 +   Copyright (C) 2008 Free Software Foundation, Inc.
@@ -29,7 +29,7 @@
 +
 +   This program is free software; you can redistribute it and/or modify
 +   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 2 of the License, or
++   the Free Software Foundation; either version 3 of the License, or
 +   (at your option) any later version.
 +
 +   This program is distributed in the hope that it will be useful,
@@ -265,9 +265,12 @@
 +
 +  if (target_read_memory (addr, rec->u.mem.val, len))
 +    {
-+      fprintf_unfiltered (gdb_stdlog,
-+                        "Process record: read memory addr = 0x%s len = %d error.\n",
-+                        paddr_nz (addr), len);
++      if (record_debug)
++      {
++        fprintf_unfiltered (gdb_stdlog,
++                            "Process record: error reading memory at addr = 0x%s len = %d.\n",
++                            paddr_nz (addr), len);
++      }
 +      xfree (rec->u.mem.val);
 +      xfree (rec);
 +      return (-1);
@@ -311,13 +314,13 @@
 +      gdb_assert (record_insn_num <= record_insn_max_num);
 +      if (record_insn_num == record_insn_max_num)
 +      {
-+        /* Ask user how to do */
++        /* Ask user what to do */
 +        if (record_stop_at_limit)
 +          {
 +            int q;
 +            if (set_terminal)
 +              target_terminal_ours ();
-+            q = yquery (_("The record instruction number
(record-insn-number) is equal to record-insn-number-max.  Do you want
to close record/replay stop when record/replay buffer becomes
full(record-stop-at-limit) then auto delete first record_t?"));
++            q = yquery (_("Do you want to auto delete previous execute
log entries when record/replay buffer becomes full
(record-stop-at-limit)?"));
 +            if (set_terminal)
 +              target_terminal_inferior ();
 +            if (q)
@@ -326,7 +329,7 @@
 +              }
 +            else
 +              {
-+                error (_("Process record: record stop the program."));
++                error (_("Process record: inferior program stopped."));
 +              }
 +          }
 +      }
@@ -363,9 +366,9 @@
 +  ret = gdbarch_process_record (gdbarch,
 +                              regcache_read_pc (record_regcache));
 +  if (ret > 0)
-+    error (_("Process record pause the program."));
++    error (_("Process record: inferior program stopped."));
 +  if (ret < 0)
-+    error (_("Process record record message error."));
++    error (_("Process record: failed to record execution log."));
 +
 +  discard_cleanups (old_cleanups);
 +
@@ -417,7 +420,7 @@
 +    }
 +  if (target_async_permitted)
 +    {
-+      error (_("Process record target can't debug the GNU/Linux
inferior in asynchronous mode (linux-async)."));
++      error (_("Process record target can't debug inferior in
asynchronous mode (target-async)."));
 +    }
 +
 +  if (!gdbarch_process_record_p (current_gdbarch))
@@ -429,7 +432,7 @@
 +  if (RECORD_IS_USED)
 +    {
 +      if (!nquery
-+        (_("Process record target already running, do you want delete the
old record log?")))
++        (_("Process record target already running, do you want to delete
the old record log?")))
 +      {
 +        return;
 +      }
@@ -554,10 +557,10 @@
 +      act.sa_flags = SA_RESTART;
 +      if (sigaction (SIGINT, &act, &old_act))
 +      {
-+        perror_with_name (_("Process record: sigaction"));
++        perror_with_name (_("Process record: sigaction failed"));
 +      }
 +      /* If GDB is in terminal_inferior, it will not get the signal.
-+         And in GDB replay mode, GDB don't need to in terminal_inferior
++         And in GDB replay mode, GDB doesn't need to in terminal_inferior
 +         because inferior will not executed.
 +         Then set it to terminal_ours to make GDB get the signal.  */
 +      target_terminal_ours ();
@@ -620,7 +623,7 @@
 +            if (target_read_memory
 +                (record_list->u.mem.addr, mem, record_list->u.mem.len))
 +              {
-+                error (_("Process record: read memory addr = 0x%s len = %d error."),
++                error (_("Process record: error reading memory at addr = 0x%s len = %d."),
 +                       paddr_nz (record_list->u.mem.addr),
 +                       record_list->u.mem.len);
 +              }
@@ -629,7 +632,7 @@
 +                 record_list->u.mem.len))
 +              {
 +                error (_
-+                       ("Process record: write memory addr = 0x%s len = %d error."),
++                       ("Process record: error writing memory at addr = 0x%s len = %d."),
 +                       paddr_nz (record_list->u.mem.addr),
 +                       record_list->u.mem.len);
 +              }
@@ -657,7 +660,7 @@
 +            if (first_record_end && execution_direction == EXEC_REVERSE)
 +              {
 +                /* When reverse excute, the first record_end is the part of
-+                   current instruction. */
++                   current instruction.  */
 +                first_record_end = 0;
 +              }
 +            else
@@ -723,7 +726,7 @@
 +
 +      if (sigaction (SIGALRM, &old_act, NULL))
 +      {
-+        perror_with_name (_("Process record: sigaction"));
++        perror_with_name (_("Process record: sigaction failed"));
 +      }
 +
 +replay_out:
@@ -754,7 +757,7 @@
 +}
 +
 +static void
-+record_detach (char *args, int from_tty)
++record_detach (struct target_ops *ops, char *args, int from_tty)
 +{
 +  if (record_debug)
 +    {
@@ -765,7 +768,7 @@
 +}
 +
 +static void
-+record_mourn_inferior (void)
++record_mourn_inferior (struct target_ops *ops)
 +{
 +  if (record_debug)
 +    {
@@ -807,7 +810,7 @@
 +        if (record_arch_list_add_reg (i))
 +          {
 +            record_list_release (record_arch_list_tail);
-+            error (_("Process record: record message error."));
++            error (_("Process record: failed to record execution log."));
 +          }
 +      }
 +    }
@@ -816,13 +819,13 @@
 +      if (record_arch_list_add_reg (regnum))
 +      {
 +        record_list_release (record_arch_list_tail);
-+        error (_("Process record: record message error."));
++        error (_("Process record: failed to record execution log."));
 +      }
 +    }
 +  if (record_arch_list_add_end (0))
 +    {
 +      record_list_release (record_arch_list_tail);
-+      error (_("Process record: record message error."));
++      error (_("Process record: failed to record execution log."));
 +    }
 +  record_list->next = record_arch_list_head;
 +  record_arch_list_head->prev = record_list;
@@ -853,13 +856,13 @@
 +          {
 +            n =
 +              nquery (_
-+                      ("Becuse GDB is in replay mode, changing the value of a register
will destroy the record from this point forward. Change all
register?"));
++                      ("Becuse GDB is in replay mode, changing the value of a register
will make the execute log unusable from this point onward.  Change all
register?"));
 +          }
 +        else
 +          {
 +            n =
 +              nquery (_
-+                      ("Becuse GDB is in replay mode, changing the value of a register
will destroy the record from this point forward. Change register
%s?"),
++                      ("Becuse GDB is in replay mode, changing the value of a register
will make the execute log unusable from this point onward.  Change
register %s?"),
 +                      gdbarch_register_name (get_regcache_arch (regcache),
 +                                             regno));
 +          }
@@ -867,7 +870,7 @@
 +        if (!n)
 +          {
 +            /* Invalidate the value of regcache that set in function
-+               "regcache_raw_write". */
++               "regcache_raw_write".  */
 +            if (regno < 0)
 +              {
 +                int i;
@@ -883,7 +886,7 @@
 +                regcache_invalidate (regcache, regno);
 +              }
 +
-+            error (_("Process record cancel the operation."));
++            error (_("Process record canceled the operation."));
 +          }
 +
 +        /* Destroy the record from here forward.  */
@@ -911,7 +914,7 @@
 +      if (RECORD_IS_REPLAY)
 +      {
 +        /* Let user choice if he want to write memory or not.  */
-+        if (!nquery (_("Because GDB is in replay mode, writing to memory
will destroy the record from this point forward.  Write memory at
address 0x%s?"),
++        if (!nquery (_("Because GDB is in replay mode, writing to memory
will make the execute log unusable from this point onward.  Write
memory at address 0x%s?"),
 +                     paddr_nz (offset)))
 +          {
 +            return -1;
@@ -930,13 +933,23 @@
 +      if (record_arch_list_add_mem (offset, len))
 +      {
 +        record_list_release (record_arch_list_tail);
-+        fprintf_unfiltered (gdb_stdlog, _("Process record: record message
error."));
++        if (record_debug)
++          {
++            fprintf_unfiltered (gdb_stdlog,
++                                _
++                                ("Process record: failed to record execution log."));
++          }
 +        return -1;
 +      }
 +      if (record_arch_list_add_end (0))
 +      {
 +        record_list_release (record_arch_list_tail);
-+        fprintf_unfiltered (gdb_stdlog, _("Process record: record message
error."));
++        if (record_debug)
++          {
++            fprintf_unfiltered (gdb_stdlog,
++                                _
++                                ("Process record: failed to record execution log."));
++          }
 +        return -1;
 +      }
 +      record_list->next = record_arch_list_head;
@@ -1040,14 +1053,14 @@
 +    {
 +      if (RECORD_IS_REPLAY)
 +      {
-+        if (!from_tty || query (_("Process record: delete the log from
this point forward and begin to record the running message at current
PC?")))
++        if (!from_tty || query (_("Delete the log from this point forward
and begin to record the running message at current PC?")))
 +          {
 +            record_list_release_next ();
 +          }
 +      }
 +      else
 +      {
-+        printf_unfiltered (_("Process record: already at end of record list.\n"));
++        printf_unfiltered (_("Already at end of record list.\n"));
 +      }
 +
 +    }
@@ -1064,7 +1077,7 @@
 +{
 +  if (RECORD_IS_USED)
 +    {
-+      if (!record_list || !from_tty || query (_("Process record:
delete recorded log and stop recording?")))
++      if (!record_list || !from_tty || query (_("Delete recorded log
and stop recording?")))
 +      {
 +        unpush_target (&record_ops);
 +      }
@@ -1082,7 +1095,7 @@
 +{
 +  if (record_insn_num > record_insn_max_num && record_insn_max_num)
 +    {
-+      printf_unfiltered (_("Process record: record instructions
number is bigger than record instructions max number.  Auto delete the
first ones.\n"));
++      printf_unfiltered (_("Record instructions number is bigger
than record instructions max number.  Auto delete the first
ones?\n"));
 +
 +      while (record_insn_num > record_insn_max_num)
 +      {
@@ -1107,7 +1120,7 @@
 +  /* Init record_maskall.  */
 +  if (sigfillset (&record_maskall) == -1)
 +    {
-+      perror_with_name (_("Process record: sigfillset"));
++      perror_with_name (_("Process record: sigfillset failed"));
 +    }
 +
 +  /* Init record_first.  */
@@ -1137,7 +1150,7 @@
 +     other affect to GDB such as call function "no_shared_libraries".
 +     So I add special commands to GDB.  */
 +  add_com ("delrecord", class_obscure, cmd_record_delete,
-+         _("When process record target running in replay mode, delete the
next running messages and begin to record the running message at
current address."));
++         _("Delete the rest of execution log and start recording it anew."));
 +  add_com_alias ("dr", "delrecord", class_obscure, 1);
 +  add_com ("stoprecord", class_obscure, cmd_record_stop,
 +         _("Stop the record/replay target."));
@@ -1146,12 +1159,12 @@
 +  /* Record instructions number limit command.  */
 +  add_setshow_boolean_cmd ("record-stop-at-limit", no_class,
 +                          &record_stop_at_limit,
-+                          _("Set record/replay stop when record/replay buffer becomes full."),
-+                          _("Show record/replay stop when record/replay buffer becomes
full."), _("\
++                          _("Set whether record/replay stop when record/replay buffer
becomes full."),
++                          _("Show whether record/replay stop when record/replay buffer
becomes full."), _("\
 +Enable is default value.\n\
-+When enable, if the record/replay buffer becomes full,\n\
-+ask user how to do.\n\
-+When disable, if the record/replay buffer becomes full,\n\
++When enabled, if the record/replay buffer becomes full,\n\
++ask user what to do.\n\
++When disabled, if the record/replay buffer becomes full,\n\
 +delete it and start new recording."), NULL, NULL, &setlist, &showlist);
 +  add_setshow_zinteger_cmd ("record-insn-number-max", no_class,
 +                          &record_insn_max_num,
@@ -1164,7 +1177,7 @@
 +  add_info ("record-insn-number", show_record_insn_number, _("\
 +Show the current number of instructions in the record/replay buffer."));
 +}
---- a/record.h
+--- a//dev/null
 +++ b/record.h
 @@ -0,0 +1,99 @@
 +/* Process record and replay target for GDB, the GNU debugger.
@@ -1175,7 +1188,7 @@
 +
 +   This program is free software; you can redistribute it and/or modify
 +   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation; either version 2 of the License, or
++   the Free Software Foundation; either version 3 of the License, or
 +   (at your option) any later version.
 +
 +   This program is distributed in the hope that it will be useful,


------------------------------------------------------------------------


--- a/Makefile.in
+++ b/Makefile.in
@@ -657,7 +657,7 @@
valarith.c valops.c valprint.c value.c varobj.c vec.c \
wrapper.c \
xml-tdesc.c xml-support.c \
- inferior.c
+ inferior.c record.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -808,7 +808,7 @@
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o
+ inferior.o record.o
TSOBS = inflow.o
--- a//dev/null
+++ b/record.c
@@ -0,0 +1,1156 @@
+/* Process record and replay target for GDB, the GNU debugger.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "target.h"
+#include "gdbcmd.h"
+#include "regcache.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "record.h"
+
+#include <signal.h>
+
+#define DEFAULT_RECORD_INSN_MAX_NUM 200000
+
+int record_debug = 0;
+
+record_t record_first;
+record_t *record_list = &record_first;
+record_t *record_arch_list_head = NULL;
+record_t *record_arch_list_tail = NULL;
+struct regcache *record_regcache = NULL;
+
+/* 1 ask user. 0 auto delete the last record_t. */
+static int record_stop_at_limit = 1;
+static int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM;
+static int record_insn_num = 0;
+
+struct target_ops record_ops;
+int record_resume_step = 0;
+static int record_get_sig = 0;
+static sigset_t record_maskall;
+static int record_not_record = 0;
+int record_will_store_registers = 0;
+
+extern struct bp_location *bp_location_chain;
+
+/* The real beneath function pointers. */
+void (*record_beneath_to_resume) (ptid_t, int, enum target_signal);
+ptid_t (*record_beneath_to_wait) (ptid_t, struct target_waitstatus *);
+void (*record_beneath_to_store_registers) (struct regcache *, int regno);
+LONGEST (*record_beneath_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);
+int (*record_beneath_to_insert_breakpoint) (struct bp_target_info *);
+int (*record_beneath_to_remove_breakpoint) (struct bp_target_info *);
+
+static void
+record_list_release (record_t * rec)
+{
+ record_t *tmp;
+
+ if (!rec)
+ return;
+
+ while (rec->next)
+ {
+ rec = rec->next;
+ }
+
+ while (rec->prev)
+ {
+ tmp = rec;
+ rec = rec->prev;
+ if (tmp->type == record_reg)
+ {
+ xfree (tmp->u.reg.val);
+ }
+ else if (tmp->type == record_mem)
+ {
+ xfree (tmp->u.mem.val);
+ }
+ xfree (tmp);
+ }
+
+ if (rec != &record_first)
+ {
+ xfree (rec);
+ }
+}
+
+static void
+record_list_release_next (void)
+{
+ record_t *rec = record_list;
+ record_t *tmp = rec->next;
+ rec->next = NULL;
+ while (tmp)
+ {
+ rec = tmp->next;
+ if (tmp->type == record_reg)
+ {
+ record_insn_num--;
+ }
+ else if (tmp->type == record_reg)
+ {
+ xfree (tmp->u.reg.val);
+ }
+ else if (tmp->type == record_mem)
+ {
+ xfree (tmp->u.mem.val);
+ }
+ xfree (tmp);
+ tmp = rec;
+ }
+}
+
+static void
+record_list_release_first (void)
+{
+ record_t *tmp = NULL;
+ enum record_type type;
+
+ if (!record_first.next)
+ {
+ return;
+ }
+
+ while (1)
+ {
+ type = record_first.next->type;
+
+ if (type == record_reg)
+ {
+ xfree (record_first.next->u.reg.val);
+ }
+ else if (type == record_mem)
+ {
+ xfree (record_first.next->u.mem.val);
+ }
+ tmp = record_first.next;
+ record_first.next = tmp->next;
+ xfree (tmp);
+
+ if (!record_first.next)
+ {
+ gdb_assert (record_insn_num == 1);
+ break;
+ }
+
+ record_first.next->prev = &record_first;
+
+ if (type == record_end)
+ {
+ break;
+ }
+ }
+
+ record_insn_num--;
+}
+
+/* Add a record_t to record_arch_list. */
+static void
+record_arch_list_add (record_t * rec)
+{
+ if (record_arch_list_tail)
+ {
+ record_arch_list_tail->next = rec;
+ rec->prev = record_arch_list_tail;
+ record_arch_list_tail = rec;
+ }
+ else
+ {
+ record_arch_list_head = rec;
+ record_arch_list_tail = rec;
+ }
+}
+
+/* Record the value of a register ("num") to record_arch_list. */
+int
+record_arch_list_add_reg (int num)
+{
+ record_t *rec;
+
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: add register num = %d to record list.\n", + num);
+ }
+
+ rec = (record_t *) xmalloc (sizeof (record_t));
+ rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_reg;
+ rec->u.reg.num = num;
+
+ regcache_raw_read (record_regcache, num, rec->u.reg.val);
+
+ record_arch_list_add (rec);
+
+ return (0);
+}
+
+/* Record the value of a region of memory whose address is "addr" and
+ length is "len" to record_arch_list. */
+
+int
+record_arch_list_add_mem (CORE_ADDR addr, int len)
+{
+ record_t *rec;
+
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: add mem addr = 0x%s len = %d to record list.\n",
+ paddr_nz (addr), len);
+ }
+
+ if (!addr)
+ {
+ return (0);
+ }
+
+ rec = (record_t *) xmalloc (sizeof (record_t));
+ rec->u.mem.val = (gdb_byte *) xmalloc (len);
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_mem;
+ rec->u.mem.addr = addr;
+ rec->u.mem.len = len;
+
+ if (target_read_memory (addr, rec->u.mem.val, len))
+ {
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: error reading memory at addr = 0x%s len = %d.\n",
+ paddr_nz (addr), len);
+ }
+ xfree (rec->u.mem.val);
+ xfree (rec);
+ return (-1);
+ }
+
+ record_arch_list_add (rec);
+
+ return (0);
+}
+
+/* Add a record_end type record_t to record_arch_list. */
+int
+record_arch_list_add_end (int need_dasm)
+{
+ record_t *rec;
+
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: add end need_dasm = %d to arch list.\n",
+ need_dasm);
+ }
+
+ rec = (record_t *) xmalloc (sizeof (record_t));
+ rec->prev = NULL;
+ rec->next = NULL;
+ rec->type = record_end;
+
+ rec->u.need_dasm = need_dasm;
+
+ record_arch_list_add (rec);
+
+ return (0);
+}
+
+static void
+record_check_insn_num (int set_terminal)
+{
+ if (record_insn_max_num)
+ {
+ gdb_assert (record_insn_num <= record_insn_max_num);
+ if (record_insn_num == record_insn_max_num)
+ {
+ /* Ask user what to do */
+ if (record_stop_at_limit)
+ {
+ int q;
+ if (set_terminal)
+ target_terminal_ours ();
+ q = yquery (_("Do you want to auto delete previous execute log entries when record/replay buffer becomes full (record-stop-at-limit)?"));
+ if (set_terminal)
+ target_terminal_inferior ();
+ if (q)
+ {
+ record_stop_at_limit = 0;
+ }
+ else
+ {
+ error (_("Process record: inferior program stopped."));
+ }
+ }
+ }
+ }
+}
+
+/* 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);
+ set_executing (inferior_ptid, 0);
+ normal_stop ();
+}
+
+void
+record_message (struct gdbarch *gdbarch)
+{
+ int ret;
+ struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
+
+ /* Check record_insn_num. */
+ record_check_insn_num (1);
+
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ record_regcache = get_current_regcache ();
+
+ ret = gdbarch_process_record (gdbarch, + regcache_read_pc (record_regcache));
+ if (ret > 0)
+ error (_("Process record: inferior program stopped."));
+ if (ret < 0)
+ error (_("Process record: failed to record execution log."));
+
+ discard_cleanups (old_cleanups);
+
+ record_list->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list;
+ record_list = record_arch_list_tail;
+
+ if (record_insn_num == record_insn_max_num && record_insn_max_num)
+ {
+ record_list_release_first ();
+ }
+ else
+ {
+ record_insn_num++;
+ }
+}
+
+/* Things to clean up if we QUIT out of function that set
+ record_not_record. */
+static void
+record_not_record_cleanups (void *ignore)
+{
+ record_not_record = 0;
+}
+
+void
+record_not_record_set (void)
+{
+ struct cleanup *old_cleanups = make_cleanup (record_not_record_cleanups, 0);
+ record_not_record = 1;
+}
+
+static void
+record_open (char *name, int from_tty)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+ }
+
+ /* check exec */
+ if (!target_has_execution)
+ {
+ error (_("Process record: the program is not being run."));
+ }
+ if (non_stop)
+ {
+ error (_("Process record target can't debug inferior in non-stop mode (non-stop)."));
+ }
+ if (target_async_permitted)
+ {
+ error (_("Process record target can't debug inferior in asynchronous mode (target-async)."));
+ }
+
+ if (!gdbarch_process_record_p (current_gdbarch))
+ {
+ error (_("Process record: the current architecture doesn't support record function."));
+ }
+
+ /* Check if record target is already running */
+ if (RECORD_IS_USED)
+ {
+ if (!nquery
+ (_("Process record target already running, do you want to delete the old record log?")))
+ {
+ return;
+ }
+ }
+
+ push_target (&record_ops);
+
+ /* Reset */
+ record_insn_num = 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 void
+record_resume (ptid_t ptid, int step, enum target_signal siggnal)
+{
+ record_resume_step = step;
+
+ if (!RECORD_IS_REPLAY)
+ {
+ record_message (current_gdbarch);
+ record_beneath_to_resume (ptid, 1, siggnal);
+ }
+}
+
+static void
+record_sig_handler (int signo)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
+ }
+ record_resume_step = 1;
+ record_get_sig = 1;
+}
+
+static void
+record_wait_cleanups (void *ignore)
+{
+ if (execution_direction == EXEC_REVERSE)
+ {
+ if (record_list->next)
+ {
+ record_list = record_list->next;
+ }
+ }
+ else
+ {
+ record_list = record_list->prev;
+ }
+ set_executing (inferior_ptid, 0);
+ normal_stop ();
+}
+
+/* record_wait
+ In replay mode, this function examines the recorded log and
+ determines where to stop. */
+
+static ptid_t
+record_wait (ptid_t ptid, struct target_waitstatus *status)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_wait record_resume_step = %d\n",
+ record_resume_step);
+ }
+
+ if (!RECORD_IS_REPLAY)
+ {
+ return record_beneath_to_wait (ptid, status);
+ }
+ else
+ {
+ struct sigaction act, old_act;
+ int need_dasm = 0;
+ struct regcache *regcache = get_current_regcache ();
+ int continue_flag = 1;
+ int first_record_end = 1;
+ struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+ CORE_ADDR tmp_pc;
+
+ status->kind = TARGET_WAITKIND_STOPPED;
+
+ /* Check breakpoint when forward execute. */
+ if (execution_direction == EXEC_FORWARD)
+ {
+ tmp_pc = regcache_read_pc (regcache);
+ if (breakpoint_inserted_here_p (tmp_pc))
+ {
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: break at 0x%s.\n",
+ paddr_nz (tmp_pc));
+ }
+ if (gdbarch_decr_pc_after_break (get_regcache_arch (regcache))
+ && !record_resume_step)
+ {
+ regcache_write_pc (regcache,
+ tmp_pc +
+ gdbarch_decr_pc_after_break
+ (get_regcache_arch (regcache)));
+ }
+ goto replay_out;
+ }
+ }
+
+ record_get_sig = 0;
+ act.sa_handler = record_sig_handler;
+ act.sa_mask = record_maskall;
+ act.sa_flags = SA_RESTART;
+ if (sigaction (SIGINT, &act, &old_act))
+ {
+ perror_with_name (_("Process record: sigaction failed"));
+ }
+ /* If GDB is in terminal_inferior, it will not get the signal.
+ And in GDB replay mode, GDB doesn't need to in terminal_inferior
+ because inferior will not executed.
+ Then set it to terminal_ours to make GDB get the signal. */
+ target_terminal_ours ();
+
+ /* In EXEC_FORWARD mode, record_list point to the tail of prev
+ instruction. */
+ if (execution_direction == EXEC_FORWARD && record_list->next)
+ {
+ record_list = record_list->next;
+ }
+
+ /* Loop over the record_list, looking for the next place to
+ stop. */
+ do
+ {
+ /* Check for beginning and end of log. */
+ if (execution_direction == EXEC_REVERSE + && record_list == &record_first)
+ {
+ /* Hit beginning of record log in reverse. */
+ status->kind = TARGET_WAITKIND_NO_HISTORY;
+ break;
+ }
+ if (execution_direction != EXEC_REVERSE && !record_list->next)
+ {
+ /* Hit end of record log going forward. */
+ status->kind = TARGET_WAITKIND_NO_HISTORY;
+ 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 0x%s to inferior num = %d.\n",
+ paddr_nz ((CORE_ADDR)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 */
+ gdb_byte *mem = alloca (record_list->u.mem.len);
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_mem 0x%s to inferior addr = 0x%s len = %d.\n",
+ paddr_nz ((CORE_ADDR)record_list),
+ paddr_nz (record_list->u.mem.addr),
+ record_list->u.mem.len);
+ }
+ if (target_read_memory
+ (record_list->u.mem.addr, mem, record_list->u.mem.len))
+ {
+ error (_("Process record: error reading memory at addr = 0x%s len = %d."),
+ paddr_nz (record_list->u.mem.addr),
+ record_list->u.mem.len);
+ }
+ if (target_write_memory
+ (record_list->u.mem.addr, record_list->u.mem.val,
+ record_list->u.mem.len))
+ {
+ error (_
+ ("Process record: error writing memory at addr = 0x%s len = %d."),
+ paddr_nz (record_list->u.mem.addr),
+ record_list->u.mem.len);
+ }
+ memcpy (record_list->u.mem.val, mem, record_list->u.mem.len);
+ }
+ else
+ {
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: record_end 0x%s to inferior need_dasm = %d.\n",
+ paddr_nz ((CORE_ADDR)record_list),
+ record_list->u.need_dasm);
+ }
+
+ if (execution_direction == EXEC_FORWARD)
+ {
+ need_dasm = record_list->u.need_dasm;
+ }
+ if (need_dasm)
+ {
+ gdbarch_process_record_dasm (current_gdbarch);
+ }
+
+ if (first_record_end && execution_direction == EXEC_REVERSE)
+ {
+ /* When reverse excute, the first record_end is the part of
+ current instruction. */
+ first_record_end = 0;
+ }
+ else
+ {
+ /* In EXEC_REVERSE mode, this is the record_end of prev
+ instruction.
+ In EXEC_FORWARD mode, this is the record_end of current
+ instruction. */
+ /* step */
+ if (record_resume_step)
+ {
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: step.\n");
+ }
+ continue_flag = 0;
+ }
+
+ /* check breakpoint */
+ tmp_pc = regcache_read_pc (regcache);
+ if (breakpoint_inserted_here_p (tmp_pc))
+ {
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: break at 0x%s.\n",
+ paddr_nz (tmp_pc));
+ }
+ if (gdbarch_decr_pc_after_break (get_regcache_arch (regcache))
+ && execution_direction == EXEC_FORWARD
+ && !record_resume_step)
+ {
+ regcache_write_pc (regcache,
+ tmp_pc +
+ gdbarch_decr_pc_after_break
+ (get_regcache_arch (regcache)));
+ }
+ continue_flag = 0;
+ }
+ }
+ if (execution_direction == EXEC_REVERSE)
+ {
+ need_dasm = record_list->u.need_dasm;
+ }
+ }
+
+next:
+ if (continue_flag)
+ {
+ if (execution_direction == EXEC_REVERSE)
+ {
+ if (record_list->prev)
+ record_list = record_list->prev;
+ }
+ else
+ {
+ if (record_list->next)
+ record_list = record_list->next;
+ }
+ }
+ }
+ while (continue_flag);
+
+ if (sigaction (SIGALRM, &old_act, NULL))
+ {
+ perror_with_name (_("Process record: sigaction failed"));
+ }
+
+replay_out:
+ if (record_get_sig)
+ {
+ status->value.sig = TARGET_SIGNAL_INT;
+ }
+ else
+ {
+ status->value.sig = TARGET_SIGNAL_TRAP;
+ }
+
+ discard_cleanups (old_cleanups);
+ }
+
+ return inferior_ptid;
+}
+
+static void
+record_disconnect (struct target_ops *target, char *args, int from_tty)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
+ }
+ unpush_target (&record_ops);
+ target_disconnect (args, from_tty);
+}
+
+static void
+record_detach (struct target_ops *ops, char *args, int from_tty)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
+ }
+ unpush_target (&record_ops);
+ target_detach (args, from_tty);
+}
+
+static void
+record_mourn_inferior (struct target_ops *ops)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_mourn_inferior\n");
+ }
+ unpush_target (&record_ops);
+ target_mourn_inferior ();
+}
+
+/* Close process record target before kill the inferior process. */
+static void
+record_kill (void)
+{
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
+ }
+ unpush_target (&record_ops);
+ target_kill ();
+}
+
+/* Record registers change (by user or by GDB) to list as an instruction. */
+static void
+record_registers_change (struct regcache *regcache, int regnum)
+{
+ /* Check record_insn_num. */
+ record_check_insn_num (0);
+
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+
+ record_regcache = get_current_regcache ();
+
+ if (regnum < 0)
+ {
+ int i;
+ for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
+ {
+ if (record_arch_list_add_reg (i))
+ {
+ record_list_release (record_arch_list_tail);
+ error (_("Process record: failed to record execution log."));
+ }
+ }
+ }
+ else
+ {
+ if (record_arch_list_add_reg (regnum))
+ {
+ record_list_release (record_arch_list_tail);
+ error (_("Process record: failed to record execution log."));
+ }
+ }
+ if (record_arch_list_add_end (0))
+ {
+ record_list_release (record_arch_list_tail);
+ error (_("Process record: failed to record execution log."));
+ }
+ record_list->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list;
+ record_list = record_arch_list_tail;
+
+ if (record_insn_num == record_insn_max_num && record_insn_max_num)
+ {
+ record_list_release_first ();
+ }
+ else
+ {
+ record_insn_num++;
+ }
+}
+
+static void
+record_store_registers (struct regcache *regcache, int regno)
+{
+ if (!record_not_record)
+ {
+ if (RECORD_IS_REPLAY)
+ {
+ int n;
+ struct cleanup *old_cleanups;
+
+ /* Let user choice if he want to write register or not. */
+ if (regno < 0)
+ {
+ n =
+ nquery (_
+ ("Becuse GDB is in replay mode, changing the value of a register will make the execute log unusable from this point onward. Change all register?"));
+ }
+ else
+ {
+ n =
+ nquery (_
+ ("Becuse GDB is in replay mode, changing the value of a register will make the execute log unusable from this point onward. Change register %s?"),
+ gdbarch_register_name (get_regcache_arch (regcache),
+ regno));
+ }
+
+ if (!n)
+ {
+ /* Invalidate the value of regcache that set in function
+ "regcache_raw_write". */
+ if (regno < 0)
+ {
+ int i;
+ for (i = 0;
+ i < gdbarch_num_regs (get_regcache_arch (regcache));
+ i++)
+ {
+ regcache_invalidate (regcache, i);
+ }
+ }
+ else
+ {
+ regcache_invalidate (regcache, regno);
+ }
+
+ error (_("Process record canceled the operation."));
+ }
+
+ /* Destroy the record from here forward. */
+ record_list_release_next ();
+ }
+
+ record_registers_change (regcache, regno);
+ }
+ record_beneath_to_store_registers (regcache, regno);
+}
+
+/* record_xfer_partial -- behavior is conditional on RECORD_IS_REPLAY.
+ In replay mode, we cannot write memory unles we are willing to + invalidate the record/replay log from this point forward. */
+
+static LONGEST
+record_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 (!record_not_record
+ && (object == TARGET_OBJECT_MEMORY
+ || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+ {
+ if (RECORD_IS_REPLAY)
+ {
+ /* Let user choice if he want to write memory or not. */
+ if (!nquery (_("Because GDB is in replay mode, writing to memory will make the execute log unusable from this point onward. Write memory at address 0x%s?"),
+ paddr_nz (offset)))
+ {
+ return -1;
+ }
+
+ /* Destroy the record from here forward. */
+ record_list_release_next ();
+ }
+
+ /* Check record_insn_num */
+ record_check_insn_num (0);
+
+ /* Record registers change to list as an instruction. */
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+ if (record_arch_list_add_mem (offset, len))
+ {
+ record_list_release (record_arch_list_tail);
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ _
+ ("Process record: failed to record execution log."));
+ }
+ return -1;
+ }
+ if (record_arch_list_add_end (0))
+ {
+ record_list_release (record_arch_list_tail);
+ if (record_debug)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ _
+ ("Process record: failed to record execution log."));
+ }
+ return -1;
+ }
+ record_list->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list;
+ record_list = record_arch_list_tail;
+
+ if (record_insn_num == record_insn_max_num && record_insn_max_num)
+ {
+ record_list_release_first ();
+ }
+ else
+ {
+ record_insn_num++;
+ }
+ }
+
+ return record_beneath_to_xfer_partial (ops, object, annex, readbuf,
+ writebuf, offset, len);
+}
+
+/* record_insert_breakpoint
+ record_remove_breakpoint
+ Behavior is conditional on RECORD_IS_REPLAY.
+ We will not actually insert or remove breakpoints when replaying. */
+
+static int
+record_insert_breakpoint (struct bp_target_info *bp_tgt)
+{
+ if (!RECORD_IS_REPLAY)
+ {
+ return record_beneath_to_insert_breakpoint (bp_tgt);
+ }
+
+ return 0;
+}
+
+static int
+record_remove_breakpoint (struct bp_target_info *bp_tgt)
+{
+ if (!RECORD_IS_REPLAY)
+ {
+ return record_beneath_to_remove_breakpoint (bp_tgt);
+ }
+
+ return 0;
+}
+
+static int
+record_can_execute_reverse (void)
+{
+ return 1;
+}
+
+static void
+init_record_ops (void)
+{
+ record_ops.to_shortname = "record";
+ record_ops.to_longname = "Process record and replay target";
+ record_ops.to_doc =
+ "Log program while executing and replay execution from log.";
+ record_ops.to_open = record_open;
+ record_ops.to_close = record_close;
+ record_ops.to_resume = record_resume;
+ record_ops.to_wait = record_wait;
+ record_ops.to_disconnect = record_disconnect;
+ record_ops.to_detach = record_detach;
+ record_ops.to_mourn_inferior = record_mourn_inferior;
+ record_ops.to_kill = record_kill;
+ record_ops.to_create_inferior = find_default_create_inferior; /* Make record suppport command "run". */
+ record_ops.to_store_registers = record_store_registers;
+ record_ops.to_xfer_partial = record_xfer_partial;
+ record_ops.to_insert_breakpoint = record_insert_breakpoint;
+ record_ops.to_remove_breakpoint = record_remove_breakpoint;
+ record_ops.to_can_execute_reverse = record_can_execute_reverse;
+ record_ops.to_stratum = record_stratum;
+ record_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)
+{
+ fprintf_filtered (file, _("Debugging of process record target is %s.\n"), value);
+}
+
+/* cmd_record_start -- alias for "target record". */
+
+static void
+cmd_record_start (char *args, int from_tty)
+{
+ execute_command ("target record", from_tty);
+}
+
+/* cmd_record_delete -- truncate the record log from the present point
+ of replay until the end. */
+
+static void
+cmd_record_delete (char *args, int from_tty)
+{
+ if (RECORD_IS_USED)
+ {
+ if (RECORD_IS_REPLAY)
+ {
+ if (!from_tty || query (_("Delete the log from this point forward and begin to record the running message at current PC?")))
+ {
+ record_list_release_next ();
+ }
+ }
+ else
+ {
+ printf_unfiltered (_("Already at end of record list.\n"));
+ }
+
+ }
+ else
+ {
+ printf_unfiltered (_("Process record is not started.\n"));
+ }
+}
+
+/* cmd_record_stop -- implement the "stoprecord" command. */
+
+static void
+cmd_record_stop (char *args, int from_tty)
+{
+ if (RECORD_IS_USED)
+ {
+ if (!record_list || !from_tty || query (_("Delete recorded log and stop recording?")))
+ {
+ unpush_target (&record_ops);
+ }
+ }
+ else
+ {
+ printf_unfiltered (_("Process record is not started.\n"));
+ }
+}
+
+/* set_record_insn_max_num -- set upper limit of record log size. */
+
+static void
+set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
+{
+ if (record_insn_num > record_insn_max_num && record_insn_max_num)
+ {
+ printf_unfiltered (_("Record instructions number is bigger than record instructions max number. Auto delete the first ones?\n"));
+
+ while (record_insn_num > record_insn_max_num)
+ {
+ record_list_release_first ();
+ }
+ }
+}
+
+/* show_record_insn_number -- print the current index
+ into the record log (number of insns recorded so far). */
+
+static void
+show_record_insn_number (char *ignore, int from_tty)
+{
+ printf_unfiltered (_("Record instruction number is %d.\n"),
+ record_insn_num);
+}
+
+void
+_initialize_record (void)
+{
+ /* Init record_maskall. */
+ if (sigfillset (&record_maskall) == -1)
+ {
+ perror_with_name (_("Process record: sigfillset failed"));
+ }
+
+ /* Init record_first. */
+ record_first.prev = NULL;
+ record_first.next = NULL;
+ record_first.type = record_end;
+ record_first.u.need_dasm = 0;
+
+ init_record_ops ();
+ add_target (&record_ops);
+
+ add_setshow_zinteger_cmd ("record", no_class, &record_debug,
+ _("Set debugging of record/replay feature."),
+ _("Show debugging of record/replay feature."),
+ _
+ ("When enabled, debugging output for record/replay feature is displayed."),
+ NULL, show_record_debug, &setdebuglist,
+ &showdebuglist);
+
+ add_com ("record", class_obscure, cmd_record_start,
+ _("Abbreviated form of \"target record\" command."));
+
+ add_com_alias ("rec", "record", class_obscure, 1);
+
+ /* XXX: I try to use some simple commands such as "disconnect" and
+ "detach" to support this functions. But these commands all have
+ other affect to GDB such as call function "no_shared_libraries".
+ So I add special commands to GDB. */
+ add_com ("delrecord", class_obscure, cmd_record_delete,
+ _("Delete the rest of execution log and start recording it anew."));
+ add_com_alias ("dr", "delrecord", class_obscure, 1);
+ add_com ("stoprecord", class_obscure, cmd_record_stop,
+ _("Stop the record/replay target."));
+ add_com_alias ("sr", "stoprecord", class_obscure, 1);
+
+ /* Record instructions number limit command. */
+ add_setshow_boolean_cmd ("record-stop-at-limit", no_class,
+ &record_stop_at_limit,
+ _("Set whether record/replay stop when record/replay buffer becomes full."),
+ _("Show whether record/replay stop when record/replay buffer becomes full."), _("\
+Enable is default value.\n\
+When enabled, if the record/replay buffer becomes full,\n\
+ask user what to do.\n\
+When disabled, if the record/replay buffer becomes full,\n\
+delete it and start new recording."), NULL, NULL, &setlist, &showlist);
+ add_setshow_zinteger_cmd ("record-insn-number-max", no_class,
+ &record_insn_max_num,
+ _("Set record/replay buffer limit."),
+ _("Show record/replay buffer limit."), _("\
+Set the maximum number of instructions to be stored in the\n\
+record/replay buffer. Zero means unlimited (default 200000)."), + set_record_insn_max_num, + NULL, &setlist, &showlist);
+ add_info ("record-insn-number", show_record_insn_number, _("\
+Show the current number of instructions in the record/replay buffer."));
+}
--- a//dev/null
+++ b/record.h
@@ -0,0 +1,99 @@
+/* Process record and replay target for GDB, the GNU debugger.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _RECORD_H_
+#define _RECORD_H_
+
+#define RECORD_IS_USED \
+ (current_target.beneath == &record_ops)
+#define RECORD_IS_REPLAY \
+ (record_list->next || execution_direction == EXEC_REVERSE)
+
+typedef struct record_reg_s
+{
+ int num;
+ gdb_byte *val;
+} record_reg_t;
+
+typedef struct record_mem_s
+{
+ CORE_ADDR addr;
+ int len;
+ gdb_byte *val;
+} record_mem_t;
+
+enum record_type
+{
+ record_end = 0,
+ record_reg,
+ record_mem
+};
+
+/* This is the core struct of record function.
+ An entity of record_t is a record of the value change of a register
+ ("record_reg") or a part of memory ("record_mem"). And Each instruction must
+ has a record_t ("record_end") that point out this is the last record_t of
+ this instruction.
+ Each record_t is linked to "record_list" by "prev" and "next". + */
+typedef struct record_s
+{
+ struct record_s *prev;
+ struct record_s *next;
+ enum record_type type;
+ union
+ {
+ /* reg */
+ record_reg_t reg;
+ /* mem */
+ record_mem_t mem;
+ /* end */
+ int need_dasm;
+ } u;
+} record_t;
+
+extern int record_debug;
+extern record_t *record_list;
+extern record_t *record_arch_list_head;
+extern record_t *record_arch_list_tail;
+extern struct regcache *record_regcache;
+
+extern struct target_ops record_ops;
+extern int record_resume_step;
+
+extern int record_arch_list_add_reg (int num);
+extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
+extern int record_arch_list_add_end (int need_dasm);
+extern void record_message (struct gdbarch *gdbarch);
+extern void record_not_record_set (void);
+
+extern void (*record_beneath_to_resume) (ptid_t, int, enum target_signal);
+extern ptid_t (*record_beneath_to_wait) (ptid_t, struct target_waitstatus *);
+extern void (*record_beneath_to_store_registers) (struct regcache *, int regno);
+extern LONGEST (*record_beneath_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);
+extern int (*record_beneath_to_insert_breakpoint) (struct bp_target_info *);
+extern int (*record_beneath_to_remove_breakpoint) (struct bp_target_info *);
+
+#endif /* _RECORD_H_ */


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