From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17687 invoked by alias); 10 Feb 2013 22:11:23 -0000 Received: (qmail 17674 invoked by uid 22791); 10 Feb 2013 22:11:22 -0000 X-SWARE-Spam-Status: No, hits=-6.2 required=5.0 tests=AWL,BAYES_00,KHOP_RCVD_UNTRUST,KHOP_SPAMHAUS_DROP,RCVD_IN_DNSWL_HI,RCVD_IN_HOSTKARMA_W,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_EG X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 10 Feb 2013 22:11:16 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r1AMBDpd031436 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sun, 10 Feb 2013 17:11:13 -0500 Received: from host2.jankratochvil.net (ovpn-116-18.ams2.redhat.com [10.36.116.18]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r1AMAx24011617 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO); Sun, 10 Feb 2013 17:11:02 -0500 Date: Sun, 10 Feb 2013 22:11:00 -0000 From: Jan Kratochvil To: markus.t.metzger@intel.com Cc: gdb-patches@sourceware.org, markus.t.metzger@gmail.com Subject: Re: [rfc 3/5] record: make it build again Message-ID: <20130210221059.GC4819@host2.jankratochvil.net> References: <1360337423-27095-1-git-send-email-markus.t.metzger@intel.com> <1360337423-27095-4-git-send-email-markus.t.metzger@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1360337423-27095-4-git-send-email-markus.t.metzger@intel.com> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2013-02/txt/msg00242.txt.bz2 On Fri, 08 Feb 2013 16:30:21 +0100, markus.t.metzger@intel.com wrote: [...] > --- a/gdb/record-full.c > +++ b/gdb/record-full.c > @@ -28,6 +28,7 @@ > #include "gdbcore.h" > #include "exec.h" > #include "record.h" > +#include "record-full.h" > #include "elf-bfd.h" > #include "gcore.h" > #include "event-loop.h" > @@ -37,7 +38,7 @@ > > #include > > -/* This module implements "target record", also known as "process > +/* This module implements "target record-full", 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. > @@ -205,6 +206,13 @@ static ULONGEST record_insn_count; > static struct target_ops record_ops; > static struct target_ops record_core_ops; > > +/* Command lists for "set/show record full". */ > +static struct cmd_list_element *set_record_full_cmdlist; > +static struct cmd_list_element *show_record_full_cmdlist; > + > +/* Command list for "record full". */ > +static struct cmd_list_element *record_full_cmdlist; > + > /* The beneath function pointers. */ > static struct target_ops *record_beneath_to_resume_ops; > static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int, > @@ -234,6 +242,10 @@ static int (*record_beneath_to_stopped_data_address) (struct target_ops *, > CORE_ADDR *); > static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *); > > +static void record_goto_insn (struct record_entry *entry, > + enum exec_direction_kind dir); > +static void record_save (char *recfilename); > + > /* Alloc and free functions for record_reg, record_mem, and record_end > entries. */ > > @@ -564,7 +576,7 @@ record_check_insn_num (int set_terminal) > target_terminal_ours (); > q = yquery (_("Do you want to auto delete previous execution " > "log entries when record/replay buffer becomes " > - "full (record stop-at-limit)?")); > + "full (record full stop-at-limit)?")); > if (set_terminal) > target_terminal_inferior (); > if (q) > @@ -1948,9 +1960,140 @@ record_execution_direction (void) > } > > static void > +record_info (void) Such functions should be called/renamed to record_full_info, they are specific for record-full.c and moreover other backends will have the same function. You can rename everything, also record_ops, record_core_ops -> record_full_*. GDB prevents using static names duplicated across files. (Maybe it comes from the time before "ambiguous linespec" start to put breakpoints on all of them.) > +{ > + struct record_entry *p; > + > + if (RECORD_IS_REPLAY) > + printf_filtered (_("Replay mode:\n")); > + else > + printf_filtered (_("Record mode:\n")); > + > + /* Find entry for first actual instruction in the log. */ > + for (p = record_first.next; > + p != NULL && p->type != record_end; > + p = p->next) > + ; > + > + /* Do we have a log at all? */ > + if (p != NULL && p->type == record_end) > + { > + /* Display instruction number for first instruction in the log. */ > + printf_filtered (_("Lowest recorded instruction number is %s.\n"), > + pulongest (p->u.end.insn_num)); > + > + /* If in replay mode, display where we are in the log. */ > + if (RECORD_IS_REPLAY) > + printf_filtered (_("Current instruction number is %s.\n"), > + pulongest (record_list->u.end.insn_num)); > + > + /* Display instruction number for last instruction in the log. */ > + printf_filtered (_("Highest recorded instruction number is %s.\n"), > + pulongest (record_insn_count)); > + > + /* Display log count. */ > + printf_filtered (_("Log contains %d instructions.\n"), > + record_insn_num); > + } > + else > + printf_filtered (_("No instructions have been logged.\n")); > + > + /* Display max log size. */ > + printf_filtered (_("Max logged instructions is %d.\n"), > + record_insn_max_num); > +} > + > +/* The "to_record_delete" target method. */ > + > +static void > +record_delete (void) > +{ > + record_list_release_following (record_list); > +} > + > +/* The "to_record_is_replaying" target method. */ > + > +static int > +record_is_replaying (void) > +{ > + return RECORD_IS_REPLAY; > +} > + > +/* Go to a specific entry. */ > + > +static void > +record_goto_entry (struct record_entry *p) > +{ > + if (p == NULL) > + error (_("Target insn not found.")); > + else if (p == record_list) > + error (_("Already at target insn.")); > + else if (p->u.end.insn_num > record_list->u.end.insn_num) > + { > + printf_filtered (_("Go forward to insn number %s\n"), > + pulongest (p->u.end.insn_num)); > + record_goto_insn (p, EXEC_FORWARD); > + } > + else > + { > + printf_filtered (_("Go backward to insn number %s\n"), > + pulongest (p->u.end.insn_num)); > + record_goto_insn (p, EXEC_REVERSE); > + } > + > + registers_changed (); > + reinit_frame_cache (); > + print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); > +} > + > +/* The "to_goto_record_begin" target method. */ > + > +static void > +record_goto_begin (void) > +{ > + struct record_entry *p = NULL; > + > + for (p = &record_first; p != NULL; p = p->next) > + if (p->type == record_end) > + break; > + > + record_goto_entry (p); > +} > + > +/* The "to_goto_record_end" target method. */ > + > +static void > +record_goto_end (void) > +{ > + struct record_entry *p = NULL; > + > + for (p = record_list; p->next != NULL; p = p->next) > + ; > + for (; p!= NULL; p = p->prev) > + if (p->type == record_end) > + break; > + > + record_goto_entry (p); > +} > + > +/* The "to_goto_record" target method. */ > + > +static void > +record_goto (ULONGEST target_insn) > +{ > + struct record_entry *p = NULL; > + > + for (p = &record_first; p != NULL; p = p->next) > + if (p->type == record_end && p->u.end.insn_num == target_insn) > + break; > + > + record_goto_entry (p); > +} > + > +static void > init_record_ops (void) > { > - record_ops.to_shortname = "record"; > + record_ops.to_shortname = "record-full"; > record_ops.to_longname = "Process record and replay target"; > record_ops.to_doc = > "Log program while executing and replay execution from log."; > @@ -1978,6 +2121,13 @@ init_record_ops (void) > record_ops.to_can_async_p = record_can_async_p; > record_ops.to_is_async_p = record_is_async_p; > record_ops.to_execution_direction = record_execution_direction; > + record_ops.to_info_record = record_info; > + record_ops.to_save_record = record_save; > + record_ops.to_delete_record = record_delete; > + record_ops.to_record_is_replaying = record_is_replaying; > + record_ops.to_goto_record_begin = record_goto_begin; > + record_ops.to_goto_record_end = record_goto_end; > + record_ops.to_goto_record = record_goto; > record_ops.to_magic = OPS_MAGIC; > } > > @@ -2203,6 +2353,12 @@ init_record_core_ops (void) > record_core_ops.to_can_async_p = record_can_async_p; > record_core_ops.to_is_async_p = record_is_async_p; > record_core_ops.to_execution_direction = record_execution_direction; > + record_core_ops.to_info_record = record_info; > + record_core_ops.to_delete_record = record_delete; > + record_core_ops.to_record_is_replaying = record_is_replaying; > + record_core_ops.to_goto_record_begin = record_goto_begin; > + record_core_ops.to_goto_record_end = record_goto_end; > + record_core_ops.to_goto_record = record_goto; > record_core_ops.to_magic = OPS_MAGIC; > } > > @@ -2507,9 +2663,8 @@ record_save_cleanups (void *data) > format, with an extra section for our data. */ > > static void > -cmd_record_save (char *args, int from_tty) > +record_save (char *recfilename) record_full_save etc. > { > - char *recfilename, recfilename_buffer[40]; > struct record_entry *cur_record_list; > uint32_t magic; > struct regcache *regcache; > @@ -2521,20 +2676,6 @@ cmd_record_save (char *args, int from_tty) > asection *osec = NULL; > int bfd_offset = 0; > > - if (strcmp (current_target.to_shortname, "record") != 0) > - error (_("This command can only be used with target 'record'.\n" > - "Use 'target record' first.\n")); > - > - if (args && *args) > - recfilename = args; > - else > - { > - /* Default recfile name is "gdb_record.PID". */ > - snprintf (recfilename_buffer, sizeof (recfilename_buffer), > - "gdb_record.%d", PIDGET (inferior_ptid)); > - recfilename = recfilename_buffer; > - } > - > /* Open the save file. */ > if (record_debug) > fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n", > @@ -2750,3 +2891,143 @@ record_goto_insn (struct record_entry *entry, > } while (record_list != entry); > do_cleanups (set_cleanups); > } > + > +/* Alias for "target record-full". */ > + > +static void > +cmd_record_start (char *args, int from_tty) cmd_record_full_start > +{ > + execute_command ("target record-full", from_tty); > +} > + > +static void > +set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c) set_record_full_insn_max_num > +{ > + if (record_insn_num > record_insn_max_num && record_insn_max_num) record_full_insn_num > + { > + /* Count down record_insn_num while releasing records from list. */ > + while (record_insn_num > record_insn_max_num) > + { > + record_list_release_first (); record_full_list_release_first etc. > + record_insn_num--; > + } > + } > +} > + > +/* The "set record full" command. */ > + > +static void > +set_record_full_command (char *args, int from_tty) > +{ > + printf_unfiltered (_("\"set record full\" must be followed " > + "by an apporpriate subcommand.\n")); > + help_list (set_record_full_cmdlist, "set record full ", all_commands, > + gdb_stdout); > +} > + > +/* The "show record full" command. */ > + > +static void > +show_record_full_command (char *args, int from_tty) > +{ > + cmd_show_list (show_record_full_cmdlist, from_tty, ""); > +} > + > +/* Provide a prototype to silence -Wmissing-prototypes. */ > +extern initialize_file_ftype _initialize_record_full; > + > +void > +_initialize_record_full (void) > +{ > + struct cmd_list_element *c; > + > + /* Init record_first. */ > + record_first.prev = NULL; > + record_first.next = NULL; > + record_first.type = record_end; > + > + init_record_ops (); > + add_target (&record_ops); > + add_deprecated_target_alias (&record_ops, "record"); > + init_record_core_ops (); > + add_target (&record_core_ops); > + > + add_prefix_cmd ("full", class_obscure, cmd_record_start, > + _("Start full execution recording."), &record_full_cmdlist, > + "record full ", 0, &record_cmdlist); > + > + c = add_cmd ("restore", class_obscure, cmd_record_restore, > + _("Restore the execution log from a file.\n\ > +Argument is filename. File must be created with 'record save'."), > + &record_full_cmdlist); > + set_cmd_completer (c, filename_completer); > + > + /* Deprecate the old version without "full" prefix. */ > + c = add_alias_cmd ("restore", "full restore", class_obscure, 1, > + &record_cmdlist); > + set_cmd_completer (c, filename_completer); > + deprecate_cmd (c, "record full restore"); This (and all add_alias_cmd below) don't display the warning as discussed before. I guess we can keep it as is as the missing warning is tracked deprecated_cmd_warning does not work for prefixed commands http://sourceware.org/bugzilla/show_bug.cgi?id=15104 and the only command where it is most visible is "target record" which you have successfully workarounded in the patchset. a bit offtopic: 'git am' somehow broken on applicating the patchset (but later it went OK by hand), it would be easier to have it in a public GIT branch, possibly in http://sourceware.org/gdb/wiki/ArcherBranchManagement needing an account http://sourceware.org/cgi-bin/pdw/ps_form.cgi which you are going to get for the later check-in anyway; or you could use github or some such site. > + > + add_prefix_cmd ("full", class_support, set_record_full_command, > + _("Set record options"), &set_record_full_cmdlist, > + "set record full ", 0, &set_record_cmdlist); > + > + add_prefix_cmd ("full", class_support, show_record_full_command, > + _("Show record options"), &show_record_full_cmdlist, > + "show record full ", 0, &show_record_cmdlist); > + > + /* Record instructions number limit command. */ > + add_setshow_boolean_cmd ("stop-at-limit", no_class, > + &record_stop_at_limit, _("\ > +Set whether record/replay stops when record/replay buffer becomes full."), _("\ > +Show whether record/replay stops when record/replay buffer becomes full."), > + _("Default is ON.\n\ > +When ON, if the record/replay buffer becomes full, ask user what to do.\n\ > +When OFF, if the record/replay buffer becomes full,\n\ > +delete the oldest recorded instruction to make room for each new one."), > + NULL, NULL, > + &set_record_full_cmdlist, &show_record_full_cmdlist); > + > + c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1, > + &set_record_cmdlist); > + deprecate_cmd (c, "set record full stop-at-limit"); > + > + c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1, > + &show_record_cmdlist); > + deprecate_cmd (c, "show record full stop-at-limit"); > + > + add_setshow_uinteger_cmd ("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 is 200000."), > + set_record_insn_max_num, > + NULL, &set_record_full_cmdlist, > + &show_record_full_cmdlist); > + > + c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1, > + &set_record_cmdlist); > + deprecate_cmd (c, "set record full insn-number-max"); > + > + c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1, > + &show_record_cmdlist); > + deprecate_cmd (c, "show record full insn-number-max"); > + > + add_setshow_boolean_cmd ("memory-query", no_class, &record_memory_query, _("\ > +Set whether query if PREC cannot record memory change of next instruction."), > + _("\ > +Show whether query if PREC cannot record memory change of next instruction."), > + _("\ > +Default is OFF.\n\ > +When ON, query if PREC cannot record memory change of next instruction."), > + NULL, NULL, > + &set_record_full_cmdlist, &show_record_full_cmdlist); > + > + c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1, > + &set_record_cmdlist); > + deprecate_cmd (c, "set record full memory-query"); > + > + c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1, > + &show_record_cmdlist); > + deprecate_cmd (c, "show record full memory-query"); > +} > diff --git a/gdb/record.c b/gdb/record.c > index 2e970ae..5293417 100644 > --- a/gdb/record.c > +++ b/gdb/record.c > @@ -19,29 +19,46 @@ > > #include "defs.h" > #include "gdbcmd.h" > -#include "regcache.h" > -#include "gdbthread.h" > -#include "event-top.h" > -#include "exceptions.h" > #include "completer.h" > -#include "arch-utils.h" > -#include "gdbcore.h" > -#include "exec.h" > #include "record.h" > -#include "elf-bfd.h" > -#include "gcore.h" > -#include "event-loop.h" > -#include "inf-loop.h" > -#include "gdb_bfd.h" > #include "observer.h" > - > -#include > +#include "inferior.h" > > /* This is the debug switch for process record. */ > unsigned int record_debug = 0; > > -/* The implementation of the command "record goto". */ > -static void cmd_record_goto (char *, int); > +struct cmd_list_element *record_cmdlist = NULL; > +struct cmd_list_element *set_record_cmdlist = NULL; > +struct cmd_list_element *show_record_cmdlist = NULL; > +struct cmd_list_element *info_record_cmdlist = NULL; > + > +/* Find the record target in the target stack. */ > + > +static struct target_ops * > +find_record_target (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_stratum == record_stratum) > + return t; > + > + return NULL; > +} > + > +/* Check that recording is active. Throw an error, if it isn't. */ > + > +static struct target_ops * > +require_record_target (void) > +{ > + struct target_ops *t; > + > + t = find_record_target (); > + if (t == NULL) > + error (_("No record target is currently active.")); cmd_record_save formerly had more suggestive: error (_("This command can only be used with target 'record'.\n" "Use 'target record' first.\n")); So require_record_target could have now: error (_("No record target is currently active.\n" "Use one of the 'target record-' commands first.\n")); > + > + return t; > +} > > /* Implement "show record debug" command. */ > > @@ -58,7 +75,7 @@ show_record_debug (struct ui_file *file, int from_tty, > static void > cmd_record_start (char *args, int from_tty) > { > - execute_command ("target record", from_tty); > + execute_command ("target record-full", from_tty); > } > > /* Truncate the record log from the present point > @@ -67,21 +84,25 @@ cmd_record_start (char *args, int from_tty) > static void > cmd_record_delete (char *args, int from_tty) > { > - if (current_target.to_stratum == record_stratum) > + require_record_target (); > + > + if (!target_record_is_replaying ()) > { > - 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_following (record_list); > - } > - else > - printf_unfiltered (_("Already at end of record list.\n")); > + printf_unfiltered (_("Already at end of record list.\n")); > + return; > + } > > + if (!target_supports_delete_record ()) > + { > + printf_unfiltered (_("The current record target does not support " > + "this operation.\n")); > + return; > } > - else > - printf_unfiltered (_("Process record is not started.\n")); > + > + if (!from_tty || query (_("Delete the log from this point forward " > + "and begin to record the running message " > + "at current PC?"))) > + target_delete_record (); > } > > /* Implement the "stoprecord" or "record stop" command. */ > @@ -89,36 +110,18 @@ cmd_record_delete (char *args, int from_tty) > static void > cmd_record_stop (char *args, int from_tty) > { > - if (current_target.to_stratum == record_stratum) > - { > - unpush_target (&record_ops); > - printf_unfiltered (_("Process record is stopped and all execution " > - "logs are deleted.\n")); > + struct target_ops *t; > > - observer_notify_record_changed (current_inferior (), 0); > - } > - else > - printf_unfiltered (_("Process record is not started.\n")); > -} > + t = require_record_target (); > + unpush_target (t); > > -/* Set upper limit of record log size. */ > + printf_unfiltered (_("Process record is stopped and all execution " > + "logs are deleted.\n")); > > -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) > - { > - /* Count down record_insn_num while releasing records from list. */ > - while (record_insn_num > record_insn_max_num) > - { > - record_list_release_first (); > - record_insn_num--; > - } > - } > + observer_notify_record_changed (current_inferior (), 0); > } > > -static struct cmd_list_element *record_cmdlist, *set_record_cmdlist, > - *show_record_cmdlist, *info_record_cmdlist; > +/* The "set record" command. */ > > static void > set_record_command (char *args, int from_tty) > @@ -128,65 +131,53 @@ set_record_command (char *args, int from_tty) > help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); > } > > +/* The "show record" command. */ > + > static void > show_record_command (char *args, int from_tty) > { > cmd_show_list (show_record_cmdlist, from_tty, ""); > } > > -/* Display some statistics about the execution log. */ > +/* The "info record" command. */ > > static void > info_record_command (char *args, int from_tty) > { > - struct record_entry *p; > + struct target_ops *t; > > - if (current_target.to_stratum == record_stratum) > + t = find_record_target (); > + if (t == NULL) > { > - if (RECORD_IS_REPLAY) > - printf_filtered (_("Replay mode:\n")); > - else > - printf_filtered (_("Record mode:\n")); > - > - /* Find entry for first actual instruction in the log. */ > - for (p = record_first.next; > - p != NULL && p->type != record_end; > - p = p->next) > - ; > - > - /* Do we have a log at all? */ > - if (p != NULL && p->type == record_end) > - { > - /* Display instruction number for first instruction in the log. */ > - printf_filtered (_("Lowest recorded instruction number is %s.\n"), > - pulongest (p->u.end.insn_num)); > - > - /* If in replay mode, display where we are in the log. */ > - if (RECORD_IS_REPLAY) > - printf_filtered (_("Current instruction number is %s.\n"), > - pulongest (record_list->u.end.insn_num)); > - > - /* Display instruction number for last instruction in the log. */ > - printf_filtered (_("Highest recorded instruction number is %s.\n"), > - pulongest (record_insn_count)); > - > - /* Display log count. */ > - printf_filtered (_("Log contains %d instructions.\n"), > - record_insn_num); > - } > - else > - { > - printf_filtered (_("No instructions have been logged.\n")); > - } > + printf_filtered (_("No record target is currently active.\n")); > + return; > } > + > + printf_filtered (_("Active record target: %s\n"), t->to_shortname); > + if (t->to_info_record) nit: According to the new GDB coding style rules it should be: if (t->to_info_record != NULL) > + t->to_info_record (); > +} > + > +/* The "record save" command. */ > + > +static void > +cmd_record_save (char *args, int from_tty) > +{ > + char *recfilename, recfilename_buffer[40]; > + > + require_record_target (); > + > + if (args && *args) > + recfilename = args; > else > { > - printf_filtered (_("target record is not active.\n")); > + /* Default recfile name is "gdb_record.PID". */ > + snprintf (recfilename_buffer, sizeof (recfilename_buffer), > + "gdb_record.%d", PIDGET (inferior_ptid)); xsnprintf, intended for better OS compatibility. > + recfilename = recfilename_buffer; > } > > - /* Display max log size. */ > - printf_filtered (_("Max logged instructions is %d.\n"), > - record_insn_max_num); > + target_save_record (recfilename); > } > > /* "record goto" command. Argument is an instruction number, > @@ -194,65 +185,26 @@ info_record_command (char *args, int from_tty) > > Rewinds the recording (forward or backward) to the given instruction. */ > > -static void > +void > cmd_record_goto (char *arg, int from_tty) > { > - struct record_entry *p = NULL; > - ULONGEST target_insn = 0; > + require_record_target (); > > if (arg == NULL || *arg == '\0') > error (_("Command requires an argument (insn number to go to).")); > > if (strncmp (arg, "start", strlen ("start")) == 0 > || strncmp (arg, "begin", strlen ("begin")) == 0) > - { > - /* Special case. Find first insn. */ > - for (p = &record_first; p != NULL; p = p->next) > - if (p->type == record_end) > - break; > - if (p) > - target_insn = p->u.end.insn_num; > - } > + target_goto_record_begin (); > else if (strncmp (arg, "end", strlen ("end")) == 0) > - { > - /* Special case. Find last insn. */ > - for (p = record_list; p->next != NULL; p = p->next) > - ; > - for (; p!= NULL; p = p->prev) > - if (p->type == record_end) > - break; > - if (p) > - target_insn = p->u.end.insn_num; > - } > + target_goto_record_end (); > else > { > - /* General case. Find designated insn. */ > - target_insn = parse_and_eval_long (arg); > + ULONGEST insn; > > - for (p = &record_first; p != NULL; p = p->next) > - if (p->type == record_end && p->u.end.insn_num == target_insn) > - break; > + insn = parse_and_eval_long (arg); > + target_goto_record (insn); > } > - > - if (p == NULL) > - error (_("Target insn '%s' not found."), arg); > - else if (p == record_list) > - error (_("Already at insn '%s'."), arg); > - else if (p->u.end.insn_num > record_list->u.end.insn_num) > - { > - printf_filtered (_("Go forward to insn number %s\n"), > - pulongest (target_insn)); > - record_goto_insn (p, EXEC_FORWARD); > - } > - else > - { > - printf_filtered (_("Go backward to insn number %s\n"), > - pulongest (target_insn)); > - record_goto_insn (p, EXEC_REVERSE); > - } > - registers_changed (); > - reinit_frame_cache (); > - print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); > } > > /* Provide a prototype to silence -Wmissing-prototypes. */ > @@ -263,16 +215,6 @@ _initialize_record (void) > { > struct cmd_list_element *c; > > - /* Init record_first. */ > - record_first.prev = NULL; > - record_first.next = NULL; > - record_first.type = record_end; > - > - init_record_ops (); > - add_target (&record_ops); > - init_record_core_ops (); > - add_target (&record_core_ops); > - > add_setshow_zuinteger_cmd ("record", no_class, &record_debug, > _("Set debugging of record/replay feature."), > _("Show debugging of record/replay feature."), > @@ -282,7 +224,7 @@ _initialize_record (void) > &showdebuglist); > > c = add_prefix_cmd ("record", class_obscure, cmd_record_start, > - _("Abbreviated form of \"target record\" command."), > + _("Start recording."), > &record_cmdlist, "record ", 0, &cmdlist); > set_cmd_completer (c, filename_completer); > > @@ -307,12 +249,6 @@ Default filename is 'gdb_record.'."), > &record_cmdlist); > set_cmd_completer (c, filename_completer); > > - c = add_cmd ("restore", class_obscure, cmd_record_restore, > - _("Restore the execution log from a file.\n\ > -Argument is filename. File must be created with 'record save'."), > - &record_cmdlist); > - set_cmd_completer (c, filename_completer); > - > add_cmd ("delete", class_obscure, cmd_record_delete, > _("Delete the rest of execution log and start recording it anew."), > &record_cmdlist); > @@ -324,40 +260,8 @@ Argument is filename. File must be created with 'record save'."), > &record_cmdlist); > add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); > > - /* Record instructions number limit command. */ > - add_setshow_boolean_cmd ("stop-at-limit", no_class, > - &record_stop_at_limit, _("\ > -Set whether record/replay stops when record/replay buffer becomes full."), _("\ > -Show whether record/replay stops when record/replay buffer becomes full."), > - _("Default is ON.\n\ > -When ON, if the record/replay buffer becomes full, ask user what to do.\n\ > -When OFF, if the record/replay buffer becomes full,\n\ > -delete the oldest recorded instruction to make room for each new one."), > - NULL, NULL, > - &set_record_cmdlist, &show_record_cmdlist); > - add_setshow_uinteger_cmd ("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 is 200000."), > - set_record_insn_max_num, > - NULL, &set_record_cmdlist, &show_record_cmdlist); > - > add_cmd ("goto", class_obscure, cmd_record_goto, _("\ > Restore the program to its state at instruction number N.\n\ > Argument is instruction number, as shown by 'info record'."), > &record_cmdlist); > - > - add_setshow_boolean_cmd ("memory-query", no_class, > - &record_memory_query, _("\ > -Set whether query if PREC cannot record memory change of next instruction."), > - _("\ > -Show whether query if PREC cannot record memory change of next instruction."), > - _("\ > -Default is OFF.\n\ > -When ON, query if PREC cannot record memory change of next instruction."), > - NULL, NULL, > - &set_record_cmdlist, &show_record_cmdlist); > - > } > diff --git a/gdb/record.h b/gdb/record.h > index 280f4ec..dfd8361 100644 > --- a/gdb/record.h > +++ b/gdb/record.h > @@ -20,8 +20,18 @@ > #ifndef _RECORD_H_ > #define _RECORD_H_ > > +struct cmd_list_element; > + > #define RECORD_IS_USED (current_target.to_stratum == record_stratum) > > extern unsigned int record_debug; > > +/* Allow record targets to add their own sub-commands. */ > +extern struct cmd_list_element *record_cmdlist; > +extern struct cmd_list_element *set_record_cmdlist; > +extern struct cmd_list_element *show_record_cmdlist; > +extern struct cmd_list_element *info_record_cmdlist; > + > +extern void cmd_record_goto (char *arg, int from_tty); > + > #endif /* _RECORD_H_ */ > diff --git a/gdb/target.c b/gdb/target.c > index 25f4629..e71ab96 100644 > --- a/gdb/target.c > +++ b/gdb/target.c > @@ -4231,6 +4231,135 @@ target_read_btrace (struct btrace_target_info *btinfo) > return NULL; > } > > +/* See target.h. */ > + > +void > +target_info_record (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_info_record != NULL) > + { > + t->to_info_record (); > + return; > + } > + > + tcomplain (); > +} > + > +/* See target.h. */ > + > +void > +target_save_record (char *filename) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_save_record != NULL) > + { > + t->to_save_record (filename); > + return; > + } > + > + tcomplain (); > +} > + > +/* See target.h. */ > + > +int > +target_supports_delete_record (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_delete_record != NULL) > + return 1; > + > + return 0; > +} > + > +/* See target.h. */ > + > +void > +target_delete_record (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_delete_record != NULL) > + { > + t->to_delete_record (); > + return; > + } > + > + tcomplain (); > +} > + > +/* See target.h. */ > + > +int > +target_record_is_replaying (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_record_is_replaying != NULL) > + return t->to_record_is_replaying (); > + > + return 0; > +} > + > +/* See target.h. */ > + > +void > +target_goto_record_begin (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_goto_record_begin != NULL) > + { > + t->to_goto_record_begin (); > + return; > + } > + > + tcomplain (); > +} > + > +/* See target.h. */ > + > +void > +target_goto_record_end (void) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_goto_record_end != NULL) > + { > + t->to_goto_record_end (); > + return; > + } > + > + tcomplain (); > +} > + > +/* See target.h. */ > + > +void > +target_goto_record (ULONGEST insn) > +{ > + struct target_ops *t; > + > + for (t = current_target.beneath; t != NULL; t = t->beneath) > + if (t->to_goto_record != NULL) > + { > + t->to_goto_record (insn); > + return; > + } > + > + tcomplain (); > +} > > static void > debug_to_prepare_to_store (struct regcache *regcache) > diff --git a/gdb/target.h b/gdb/target.h > index 1d73336..e4fe5da 100644 > --- a/gdb/target.h > +++ b/gdb/target.h > @@ -876,6 +876,27 @@ struct target_ops > /* Read branch trace data. */ > VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *); > > + /* Print information about the recording. */ > + void (*to_info_record) (void); > + > + /* Save the recorded execution trace into a file. */ > + void (*to_save_record) (char *filename); > + > + /* Delete the recorded execution trace from the current position onwards. */ > + void (*to_delete_record) (void); > + > + /* Query if the record target is currently replaying. */ > + int (*to_record_is_replaying) (void); > + > + /* Go to the begin of the execution trace. */ > + void (*to_goto_record_begin) (void); > + > + /* Go to the end of the execution trace. */ > + void (*to_goto_record_end) (void); > + > + /* Go to a specific location in the recorded execution trace. */ > + void (*to_goto_record) (ULONGEST); Just a nit, here could be 'ULONGEST insn'. > + > int to_magic; > /* Need sub-structure for target machine related rather than comm related? > */ > @@ -1938,5 +1959,28 @@ extern int target_btrace_has_changed (struct btrace_target_info *btinfo); > Returns a vector of branch trace blocks with the latest entry at index 0. */ > extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *); > > +/* Print record information for this record target. */ > +extern void target_info_record (void); Please do not use comments for these target_* declarations, they duplicate those of their to_* fields and they could become out of sync. As you commented the to_* fields just write: /* See to_info_record in struct target_ops. */ > + > +/* Save the recorded execution trace into a file. */ > +extern void target_save_record (char *filename); > + > +/* Query if the target supports deleting the execution log. */ > +extern int target_supports_delete_record (void); > + > +/* Delete the recorded execution trace from the current position onwards. */ > +extern void target_delete_record (void); > + > +/* Query if the record target is currently replaying. */ > +extern int target_record_is_replaying (void); > + > +/* Go to the begin of the execution trace. */ > +extern void target_goto_record_begin (void); > + > +/* Go to the end of the execution trace. */ > +extern void target_goto_record_end (void); > + > +/* Go to a specific location in the recorded execution trace. */ > +extern void target_goto_record (ULONGEST); Just a nit, here could be 'ULONGEST insn'. > > #endif /* !defined (TARGET_H) */ > -- > 1.7.0.7