From 6df6766768f1589f81cd4d0fb9e8ccc700cfd8df Mon Sep 17 00:00:00 2001 From: Michael Snyder Date: Tue, 20 Oct 2009 23:06:13 +0000 Subject: [PATCH] 2009-10-20 Michael Snyder * record.c: Add some top-level comments for general explanation. --- gdb/ChangeLog | 4 +++ gdb/record.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c145146605b..d586e9f3201 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,7 @@ +2009-10-20 Michael Snyder + + * record.c: Add some top-level comments for general explanation. + 2009-10-20 Hui Zhu Michael Snyder diff --git a/gdb/record.c b/gdb/record.c index 5174e119d30..a1b967865f7 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -27,6 +27,26 @@ #include +/* This module implements "target record", also known as "process + record and replay". This target sits on top of a "normal" target + (a target that "has execution"), and provides a record and replay + functionality, including reverse debugging. + + Target record has two modes: recording, and replaying. + + In record mode, we intercept the to_resume and to_wait methods. + Whenever gdb resumes the target, we run the target in single step + mode, and we build up an execution log in which, for each executed + instruction, we record all changes in memory and register state. + This is invisible to the user, to whom it just looks like an + ordinary debugging session (except for performance degredation). + + In replay mode, instead of actually letting the inferior run as a + process, we simulate its execution by playing back the recorded + execution log. For each instruction in the log, we simulate the + instruction's side effects by duplicating the changes that it would + have made on memory and registers. */ + #define DEFAULT_RECORD_INSN_MAX_NUM 200000 #define RECORD_IS_REPLAY \ @@ -80,6 +100,30 @@ enum record_type record_mem }; +/* This is the data structure that makes up the execution log. + + The execution log consists of a single linked list of entries + of type "struct record_entry". It is doubly linked so that it + can be traversed in either direction. + + The start of the list is anchored by a struct called + "record_first". The pointer "record_list" either points to the + last entry that was added to the list (in record mode), or to the + next entry in the list that will be executed (in replay mode). + + Each list element (struct record_entry), in addition to next and + prev pointers, consists of a union of three entry types: mem, reg, + and end. A field called "type" determines which entry type is + represented by a given list element. + + Each instruction that is added to the execution log is represented + by a variable number of list elements ('entries'). The instruction + will have one "reg" entry for each register that is changed by + executing the instruction (including the PC in every case). It + will also have one "mem" entry for each memory change. Finally, + each instruction will have an "end" entry that separates it from + the changes associated with the next instruction. */ + struct record_entry { struct record_entry *prev; @@ -99,7 +143,22 @@ struct record_entry /* This is the debug switch for process record. */ int record_debug = 0; -/* These list is for execution log. */ +/* The following variables are used for managing the linked list that + represents the execution log. + + record_first is the anchor that holds down the beginning of the list. + + record_list serves two functions: + 1) In record mode, it anchors the end of the list. + 2) In replay mode, it traverses the list and points to + the next instruction that must be emulated. + + record_arch_list_head and record_arch_list_tail are used to manage + a separate list, which is used to build up the change elements of + the currently executing instruction during record mode. When this + instruction has been completely annotated in the "arch list", it + will be appended to the main execution log. */ + static struct record_entry record_first; static struct record_entry *record_list = &record_first; static struct record_entry *record_arch_list_head = NULL; @@ -659,6 +718,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, } } +/* "to_open" target method. Open the process record target. */ + static void record_open (char *name, int from_tty) { @@ -743,6 +804,8 @@ record_open (char *name, int from_tty) record_list->next = NULL; } +/* "to_close" target method. Close the process record target. */ + static void record_close (int quitting) { @@ -755,6 +818,8 @@ record_close (int quitting) static int record_resume_step = 0; static int record_resume_error; +/* "to_resume" target method. Resume the process record target. */ + static void record_resume (struct target_ops *ops, ptid_t ptid, int step, enum target_signal signal) @@ -779,6 +844,8 @@ record_resume (struct target_ops *ops, ptid_t ptid, int step, static int record_get_sig = 0; +/* SIGINT signal handler, registered by "to_wait" method. */ + static void record_sig_handler (int signo) { @@ -805,8 +872,18 @@ record_wait_cleanups (void *ignore) record_list = record_list->prev; } -/* In replay mode, this function examines the recorded log and - determines where to stop. */ +/* "to_wait" target method for process record target. + + In record mode, the target is always run in singlestep mode + (even when gdb says to continue). The to_wait method intercepts + the stop events and determines which ones are to be passed on to + gdb. Most stop events are just singlestep events that gdb is not + to know about, so the to_wait method just records them and keeps + singlestepping. + + In replay mode, this function emulates the recorded execution log, + one instruction at a time (forward or backward), and determines + where to stop. */ static ptid_t record_wait (struct target_ops *ops, @@ -1045,6 +1122,8 @@ replay_out: return inferior_ptid; } +/* "to_disconnect" method for process record target. */ + static void record_disconnect (struct target_ops *target, char *args, int from_tty) { @@ -1055,6 +1134,8 @@ record_disconnect (struct target_ops *target, char *args, int from_tty) target_disconnect (args, from_tty); } +/* "to_detach" method for process record target. */ + static void record_detach (struct target_ops *ops, char *args, int from_tty) { @@ -1065,6 +1146,8 @@ record_detach (struct target_ops *ops, char *args, int from_tty) target_detach (args, from_tty); } +/* "to_mourn_inferior" method for process record target. */ + static void record_mourn_inferior (struct target_ops *ops) { @@ -1134,6 +1217,8 @@ record_registers_change (struct regcache *regcache, int regnum) record_insn_num++; } +/* "to_store_registers" method for process record target. */ + static void record_store_registers (struct target_ops *ops, struct regcache *regcache, int regno) @@ -1273,6 +1358,8 @@ record_insert_breakpoint (struct gdbarch *gdbarch, return 0; } +/* "to_remove_breakpoint" method for process record target. */ + static int record_remove_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) @@ -1290,6 +1377,7 @@ record_remove_breakpoint (struct gdbarch *gdbarch, return 0; } +/* "to_can_execute_reverse" method for process record target. */ static int record_can_execute_reverse (void) { @@ -1321,6 +1409,8 @@ init_record_ops (void) record_ops.to_magic = OPS_MAGIC; } +/* Implement "show record debug" command. */ + static void show_record_debug (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) @@ -1360,7 +1450,7 @@ cmd_record_delete (char *args, int from_tty) printf_unfiltered (_("Process record is not started.\n")); } -/* Implement the "stoprecord" command. */ +/* Implement the "stoprecord" or "record stop" command. */ static void cmd_record_stop (char *args, int from_tty) -- 2.30.2