]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/record.c
gdb: add back declarations for _initialize functions
[thirdparty/binutils-gdb.git] / gdb / record.c
index 8b447176e3ba96b08184158fb98329cbe19d6930..94600eb5e7ee48ed1ee4b8ab79406eb1112fbdd2 100644 (file)
@@ -1,6 +1,6 @@
 /* Process record and replay target for GDB, the GNU debugger.
 
-   Copyright (C) 2008-2013 Free Software Foundation, Inc.
+   Copyright (C) 2008-2020 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "gdbcmd.h"
 #include "completer.h"
 #include "record.h"
-#include "observer.h"
+#include "observable.h"
 #include "inferior.h"
-#include "common/common-utils.h"
+#include "gdbsupport/common-utils.h"
+#include "cli/cli-utils.h"
+#include "disasm.h"
+
+#include <ctype.h>
 
 /* This is the debug switch for process record.  */
 unsigned int record_debug = 0;
 
+/* The number of instructions to print in "record instruction-history".  */
+static unsigned int record_insn_history_size = 10;
+
+/* The variable registered as control variable in the "record
+   instruction-history" command.  Necessary for extra input
+   validation.  */
+static unsigned int record_insn_history_size_setshow_var;
+
+/* The number of functions to print in "record function-call-history".  */
+static unsigned int record_call_history_size = 10;
+
+/* The variable registered as control variable in the "record
+   call-history" command.  Necessary for extra input validation.  */
+static unsigned int record_call_history_size_setshow_var;
+
 struct cmd_list_element *record_cmdlist = NULL;
+struct cmd_list_element *record_goto_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.  */
+#define DEBUG(msg, args...)                                            \
+  if (record_debug)                                                    \
+    fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)
 
-static struct target_ops *
+/* See record.h.  */
+
+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;
+  return find_target_at (record_stratum);
 }
 
 /* Check that recording is active.  Throw an error, if it isn't.  */
@@ -57,13 +75,66 @@ require_record_target (void)
   t = find_record_target ();
   if (t == NULL)
     error (_("No record target is currently active.\n"
-            "Use one of the \"target record-<tab><tab>\" commands first."));
+            "Use one of the \"target record-<TAB><TAB>\" commands first."));
 
   return t;
 }
 
 /* See record.h.  */
 
+void
+record_preopen (void)
+{
+  /* Check if a record target is already running.  */
+  if (find_record_target () != NULL)
+    error (_("The process is already being recorded.  Use \"record stop\" to "
+            "stop recording first."));
+}
+
+/* See record.h.  */
+
+void
+record_start (const char *method, const char *format, int from_tty)
+{
+  if (method == NULL)
+    {
+      if (format == NULL)
+       execute_command_to_string ("record", from_tty, false);
+      else
+       error (_("Invalid format."));
+    }
+  else if (strcmp (method, "full") == 0)
+    {
+      if (format == NULL)
+       execute_command_to_string ("record full", from_tty, false);
+      else
+       error (_("Invalid format."));
+    }
+  else if (strcmp (method, "btrace") == 0)
+    {
+      if (format == NULL)
+       execute_command_to_string ("record btrace", from_tty, false);
+      else if (strcmp (format, "bts") == 0)
+       execute_command_to_string ("record btrace bts", from_tty, false);
+      else if (strcmp (format, "pt") == 0)
+       execute_command_to_string ("record btrace pt", from_tty, false);
+      else
+       error (_("Invalid format."));
+    }
+  else
+    error (_("Invalid method."));
+}
+
+/* See record.h.  */
+
+void
+record_stop (int from_tty)
+{
+  execute_command_to_string ("record stop", from_tty, false);
+}
+
+/* See record.h.  */
+
 int
 record_read_memory (struct gdbarch *gdbarch,
                    CORE_ADDR memaddr, gdb_byte *myaddr,
@@ -71,13 +142,115 @@ record_read_memory (struct gdbarch *gdbarch,
 {
   int ret = target_read_memory (memaddr, myaddr, len);
 
-  if (ret && record_debug)
-    printf_unfiltered (_("Process record: error reading memory "
-                        "at addr %s len = %ld.\n"),
-                      paddress (gdbarch, memaddr), (long) len);
+  if (ret != 0)
+    DEBUG ("error reading memory at addr %s len = %ld.\n",
+          paddress (gdbarch, memaddr), (long) len);
+
   return ret;
 }
 
+/* Stop recording.  */
+
+static void
+record_stop (struct target_ops *t)
+{
+  DEBUG ("stop %s", t->shortname ());
+
+  t->stop_recording ();
+}
+
+/* Unpush the record target.  */
+
+static void
+record_unpush (struct target_ops *t)
+{
+  DEBUG ("unpush %s", t->shortname ());
+
+  unpush_target (t);
+}
+
+/* See record.h.  */
+
+void
+record_disconnect (struct target_ops *t, const char *args, int from_tty)
+{
+  gdb_assert (t->stratum () == record_stratum);
+
+  DEBUG ("disconnect %s", t->shortname ());
+
+  record_stop (t);
+  record_unpush (t);
+
+  target_disconnect (args, from_tty);
+}
+
+/* See record.h.  */
+
+void
+record_detach (struct target_ops *t, inferior *inf, int from_tty)
+{
+  gdb_assert (t->stratum () == record_stratum);
+
+  DEBUG ("detach %s", t->shortname ());
+
+  record_stop (t);
+  record_unpush (t);
+
+  target_detach (inf, from_tty);
+}
+
+/* See record.h.  */
+
+void
+record_mourn_inferior (struct target_ops *t)
+{
+  gdb_assert (t->stratum () == record_stratum);
+
+  DEBUG ("mourn inferior %s", t->shortname ());
+
+  /* It is safer to not stop recording.  Resources will be freed when
+     threads are discarded.  */
+  record_unpush (t);
+
+  target_mourn_inferior (inferior_ptid);
+}
+
+/* See record.h.  */
+
+void
+record_kill (struct target_ops *t)
+{
+  gdb_assert (t->stratum () == record_stratum);
+
+  DEBUG ("kill %s", t->shortname ());
+
+  /* It is safer to not stop recording.  Resources will be freed when
+     threads are discarded.  */
+  record_unpush (t);
+
+  target_kill ();
+}
+
+/* See record.h.  */
+
+int
+record_check_stopped_by_breakpoint (const address_space *aspace,
+                                   CORE_ADDR pc,
+                                   enum target_stop_reason *reason)
+{
+  if (breakpoint_inserted_here_p (aspace, pc))
+    {
+      if (hardware_breakpoint_inserted_here_p (aspace, pc))
+       *reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+      else
+       *reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+      return 1;
+    }
+
+  *reason = TARGET_STOPPED_BY_NO_REASON;
+  return 0;
+}
+
 /* Implement "show record debug" command.  */
 
 static void
@@ -91,7 +264,7 @@ show_record_debug (struct ui_file *file, int from_tty,
 /* Alias for "target record".  */
 
 static void
-cmd_record_start (char *args, int from_tty)
+cmd_record_start (const char *args, int from_tty)
 {
   execute_command ("target record-full", from_tty);
 }
@@ -100,11 +273,11 @@ cmd_record_start (char *args, int from_tty)
    of replay until the end.  */
 
 static void
-cmd_record_delete (char *args, int from_tty)
+cmd_record_delete (const char *args, int from_tty)
 {
   require_record_target ();
 
-  if (!target_record_is_replaying ())
+  if (!target_record_is_replaying (inferior_ptid))
     {
       printf_unfiltered (_("Already at end of record list.\n"));
       return;
@@ -126,33 +299,35 @@ cmd_record_delete (char *args, int from_tty)
 /* Implement the "stoprecord" or "record stop" command.  */
 
 static void
-cmd_record_stop (char *args, int from_tty)
+cmd_record_stop (const char *args, int from_tty)
 {
   struct target_ops *t;
 
   t = require_record_target ();
-  unpush_target (t);
+
+  record_stop (t);
+  record_unpush (t);
 
   printf_unfiltered (_("Process record is stopped and all execution "
                       "logs are deleted.\n"));
 
-  observer_notify_record_changed (current_inferior (), 0);
+  gdb::observers::record_changed.notify (current_inferior (), 0, NULL, NULL);
 }
 
 /* The "set record" command.  */
 
 static void
-set_record_command (char *args, int from_tty)
+set_record_command (const char *args, int from_tty)
 {
   printf_unfiltered (_("\"set record\" must be followed "
-                      "by an apporpriate subcommand.\n"));
+                      "by an appropriate subcommand.\n"));
   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)
+show_record_command (const char *args, int from_tty)
 {
   cmd_show_list (show_record_cmdlist, from_tty, "");
 }
@@ -160,7 +335,7 @@ show_record_command (char *args, int from_tty)
 /* The "info record" command.  */
 
 static void
-info_record_command (char *args, int from_tty)
+info_record_command (const char *args, int from_tty)
 {
   struct target_ops *t;
 
@@ -171,17 +346,17 @@ info_record_command (char *args, int from_tty)
       return;
     }
 
-  printf_filtered (_("Active record target: %s\n"), t->to_shortname);
-  if (t->to_info_record != NULL)
-    t->to_info_record ();
+  printf_filtered (_("Active record target: %s\n"), t->shortname ());
+  t->info_record ();
 }
 
 /* The "record save" command.  */
 
 static void
-cmd_record_save (char *args, int from_tty)
+cmd_record_save (const char *args, int from_tty)
 {
-  char *recfilename, recfilename_buffer[40];
+  const char *recfilename;
+  char recfilename_buffer[40];
 
   require_record_target ();
 
@@ -191,45 +366,415 @@ cmd_record_save (char *args, int from_tty)
     {
       /* Default recfile name is "gdb_record.PID".  */
       xsnprintf (recfilename_buffer, sizeof (recfilename_buffer),
-                "gdb_record.%d", PIDGET (inferior_ptid));
+                "gdb_record.%d", inferior_ptid.pid ());
       recfilename = recfilename_buffer;
     }
 
   target_save_record (recfilename);
 }
 
+/* See record.h.  */
+
+void
+record_goto (const char *arg)
+{
+  ULONGEST insn;
+
+  if (arg == NULL || *arg == '\0')
+    error (_("Command requires an argument (insn number to go to)."));
+
+  insn = parse_and_eval_long (arg);
+
+  require_record_target ();
+  target_goto_record (insn);
+}
+
 /* "record goto" command.  Argument is an instruction number,
    as given by "info record".
 
    Rewinds the recording (forward or backward) to the given instruction.  */
 
-void
-cmd_record_goto (char *arg, int from_tty)
+static void
+cmd_record_goto (const char *arg, int from_tty)
+{
+  record_goto (arg);
+}
+
+/* The "record goto begin" command.  */
+
+static void
+cmd_record_goto_begin (const char *arg, int from_tty)
 {
+  if (arg != NULL && *arg != '\0')
+    error (_("Junk after argument: %s."), arg);
+
   require_record_target ();
+  target_goto_record_begin ();
+}
 
-  if (arg == NULL || *arg == '\0')
-    error (_("Command requires an argument (insn number to go to)."));
+/* The "record goto end" command.  */
+
+static void
+cmd_record_goto_end (const char *arg, int from_tty)
+{
+  if (arg != NULL && *arg != '\0')
+    error (_("Junk after argument: %s."), arg);
+
+  require_record_target ();
+  target_goto_record_end ();
+}
+
+/* Read an instruction number from an argument string.  */
+
+static ULONGEST
+get_insn_number (const char **arg)
+{
+  ULONGEST number;
+  const char *begin, *end, *pos;
+
+  begin = *arg;
+  pos = skip_spaces (begin);
+
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  number = strtoulst (pos, &end, 10);
+
+  *arg += (end - begin);
+
+  return number;
+}
+
+/* Read a context size from an argument string.  */
+
+static int
+get_context_size (const char **arg)
+{
+  const char *pos;
+  char *end;
+
+  pos = skip_spaces (*arg);
+
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  long result = strtol (pos, &end, 10);
+  *arg = end;
+  return result;
+}
+
+/* Complain about junk at the end of an argument string.  */
+
+static void
+no_chunk (const char *arg)
+{
+  if (*arg != 0)
+    error (_("Junk after argument: %s."), arg);
+}
+
+/* Read instruction-history modifiers from an argument string.  */
+
+static gdb_disassembly_flags
+get_insn_history_modifiers (const char **arg)
+{
+  gdb_disassembly_flags modifiers;
+  const char *args;
+
+  modifiers = 0;
+  args = *arg;
+
+  if (args == NULL)
+    return modifiers;
+
+  while (*args == '/')
+    {
+      ++args;
+
+      if (*args == '\0')
+       error (_("Missing modifier."));
+
+      for (; *args; ++args)
+       {
+         if (isspace (*args))
+           break;
+
+         if (*args == '/')
+           continue;
+
+         switch (*args)
+           {
+           case 'm':
+           case 's':
+             modifiers |= DISASSEMBLY_SOURCE;
+             modifiers |= DISASSEMBLY_FILENAME;
+             break;
+           case 'r':
+             modifiers |= DISASSEMBLY_RAW_INSN;
+             break;
+           case 'f':
+             modifiers |= DISASSEMBLY_OMIT_FNAME;
+             break;
+           case 'p':
+             modifiers |= DISASSEMBLY_OMIT_PC;
+             break;
+           default:
+             error (_("Invalid modifier: %c."), *args);
+           }
+       }
+
+      args = skip_spaces (args);
+    }
+
+  /* Update the argument string.  */
+  *arg = args;
+
+  return modifiers;
+}
+
+/* The "set record instruction-history-size / set record
+   function-call-history-size" commands are unsigned, with UINT_MAX
+   meaning unlimited.  The target interfaces works with signed int
+   though, to indicate direction, so map "unlimited" to INT_MAX, which
+   is about the same as unlimited in practice.  If the user does have
+   a log that huge, she can fetch it in chunks across several requests,
+   but she'll likely have other problems first...  */
+
+static int
+command_size_to_target_size (unsigned int size)
+{
+  gdb_assert (size <= INT_MAX || size == UINT_MAX);
+
+  if (size == UINT_MAX)
+    return INT_MAX;
+  else
+    return size;
+}
+
+/* The "record instruction-history" command.  */
+
+static void
+cmd_record_insn_history (const char *arg, int from_tty)
+{
+  require_record_target ();
+
+  gdb_disassembly_flags flags = get_insn_history_modifiers (&arg);
+
+  int size = command_size_to_target_size (record_insn_history_size);
+
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    target_insn_history (size, flags);
+  else if (strcmp (arg, "-") == 0)
+    target_insn_history (-size, flags);
+  else
+    {
+      ULONGEST begin, end;
+
+      begin = get_insn_number (&arg);
+
+      if (*arg == ',')
+       {
+         arg = skip_spaces (++arg);
+
+         if (*arg == '+')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
 
-  if (strncmp (arg, "start", strlen ("start")) == 0
-      || strncmp (arg, "begin", strlen ("begin")) == 0)
-    target_goto_record_begin ();
-  else if (strncmp (arg, "end", strlen ("end")) == 0)
-    target_goto_record_end ();
+             target_insn_history_from (begin, size, flags);
+           }
+         else if (*arg == '-')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             target_insn_history_from (begin, -size, flags);
+           }
+         else
+           {
+             end = get_insn_number (&arg);
+
+             no_chunk (arg);
+
+             target_insn_history_range (begin, end, flags);
+           }
+       }
+      else
+       {
+         no_chunk (arg);
+
+         target_insn_history_from (begin, size, flags);
+       }
+
+      dont_repeat ();
+    }
+}
+
+/* Read function-call-history modifiers from an argument string.  */
+
+static record_print_flags
+get_call_history_modifiers (const char **arg)
+{
+  record_print_flags modifiers = 0;
+  const char *args = *arg;
+
+  if (args == NULL)
+    return modifiers;
+
+  while (*args == '/')
+    {
+      ++args;
+
+      if (*args == '\0')
+       error (_("Missing modifier."));
+
+      for (; *args; ++args)
+       {
+         if (isspace (*args))
+           break;
+
+         if (*args == '/')
+           continue;
+
+         switch (*args)
+           {
+           case 'l':
+             modifiers |= RECORD_PRINT_SRC_LINE;
+             break;
+           case 'i':
+             modifiers |= RECORD_PRINT_INSN_RANGE;
+             break;
+           case 'c':
+             modifiers |= RECORD_PRINT_INDENT_CALLS;
+             break;
+           default:
+             error (_("Invalid modifier: %c."), *args);
+           }
+       }
+
+      args = skip_spaces (args);
+    }
+
+  /* Update the argument string.  */
+  *arg = args;
+
+  return modifiers;
+}
+
+/* The "record function-call-history" command.  */
+
+static void
+cmd_record_call_history (const char *arg, int from_tty)
+{
+  require_record_target ();
+
+  record_print_flags flags = get_call_history_modifiers (&arg);
+
+  int size = command_size_to_target_size (record_call_history_size);
+
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    target_call_history (size, flags);
+  else if (strcmp (arg, "-") == 0)
+    target_call_history (-size, flags);
   else
     {
-      ULONGEST insn;
+      ULONGEST begin, end;
+
+      begin = get_insn_number (&arg);
+
+      if (*arg == ',')
+       {
+         arg = skip_spaces (++arg);
+
+         if (*arg == '+')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
 
-      insn = parse_and_eval_long (arg);
-      target_goto_record (insn);
+             target_call_history_from (begin, size, flags);
+           }
+         else if (*arg == '-')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             target_call_history_from (begin, -size, flags);
+           }
+         else
+           {
+             end = get_insn_number (&arg);
+
+             no_chunk (arg);
+
+             target_call_history_range (begin, end, flags);
+           }
+       }
+      else
+       {
+         no_chunk (arg);
+
+         target_call_history_from (begin, size, flags);
+       }
+
+      dont_repeat ();
     }
 }
 
-/* Provide a prototype to silence -Wmissing-prototypes.  */
-extern initialize_file_ftype _initialize_record;
+/* Helper for "set record instruction-history-size" and "set record
+   function-call-history-size" input validation.  COMMAND_VAR is the
+   variable registered in the command as control variable.  *SETTING
+   is the real setting the command allows changing.  */
+
+static void
+validate_history_size (unsigned int *command_var, unsigned int *setting)
+{
+  if (*command_var != UINT_MAX && *command_var > INT_MAX)
+    {
+      unsigned int new_value = *command_var;
+
+      /* Restore previous value.  */
+      *command_var = *setting;
+      error (_("integer %u out of range"), new_value);
+    }
+
+  /* Commit new value.  */
+  *setting = *command_var;
+}
+
+/* Called by do_setshow_command.  We only want values in the
+   [0..INT_MAX] range, while the command's machinery accepts
+   [0..UINT_MAX].  See command_size_to_target_size.  */
 
+static void
+set_record_insn_history_size (const char *args, int from_tty,
+                             struct cmd_list_element *c)
+{
+  validate_history_size (&record_insn_history_size_setshow_var,
+                        &record_insn_history_size);
+}
+
+/* Called by do_setshow_command.  We only want values in the
+   [0..INT_MAX] range, while the command's machinery accepts
+   [0..UINT_MAX].  See command_size_to_target_size.  */
+
+static void
+set_record_call_history_size (const char *args, int from_tty,
+                             struct cmd_list_element *c)
+{
+  validate_history_size (&record_call_history_size_setshow_var,
+                        &record_call_history_size);
+}
+
+void _initialize_record ();
 void
-_initialize_record (void)
+_initialize_record ()
 {
   struct cmd_list_element *c;
 
@@ -241,6 +786,22 @@ _initialize_record (void)
                             NULL, show_record_debug, &setdebuglist,
                             &showdebuglist);
 
+  add_setshow_uinteger_cmd ("instruction-history-size", no_class,
+                           &record_insn_history_size_setshow_var, _("\
+Set number of instructions to print in \"record instruction-history\"."), _("\
+Show number of instructions to print in \"record instruction-history\"."), _("\
+A size of \"unlimited\" means unlimited instructions.  The default is 10."),
+                           set_record_insn_history_size, NULL,
+                           &set_record_cmdlist, &show_record_cmdlist);
+
+  add_setshow_uinteger_cmd ("function-call-history-size", no_class,
+                           &record_call_history_size_setshow_var, _("\
+Set number of function to print in \"record function-call-history\"."), _("\
+Show number of functions to print in \"record function-call-history\"."), _("\
+A size of \"unlimited\" means unlimited lines.  The default is 10."),
+                           set_record_call_history_size, NULL,
+                           &set_record_cmdlist, &show_record_cmdlist);
+
   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
                      _("Start recording."),
                      &record_cmdlist, "record ", 0, &cmdlist);
@@ -248,22 +809,22 @@ _initialize_record (void)
 
   add_com_alias ("rec", "record", class_obscure, 1);
   add_prefix_cmd ("record", class_support, set_record_command,
-                 _("Set record options"), &set_record_cmdlist,
+                 _("Set record options."), &set_record_cmdlist,
                  "set record ", 0, &setlist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
   add_prefix_cmd ("record", class_support, show_record_command,
-                 _("Show record options"), &show_record_cmdlist,
+                 _("Show record options."), &show_record_cmdlist,
                  "show record ", 0, &showlist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
   add_prefix_cmd ("record", class_support, info_record_command,
-                 _("Info record options"), &info_record_cmdlist,
+                 _("Info record options."), &info_record_cmdlist,
                  "info record ", 0, &infolist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
 
   c = add_cmd ("save", class_obscure, cmd_record_save,
               _("Save the execution log to a file.\n\
-Argument is optional filename.\n\
-Default filename is 'gdb_record.<process_id>'."),
+Usage: record save [FILENAME]\n\
+Default filename is 'gdb_record.PROCESS_ID'."),
               &record_cmdlist);
   set_cmd_completer (c, filename_completer);
 
@@ -278,8 +839,61 @@ Default filename is 'gdb_record.<process_id>'."),
            &record_cmdlist);
   add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
 
-  add_cmd ("goto", class_obscure, cmd_record_goto, _("\
+  add_prefix_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);
+                 &record_goto_cmdlist, "record goto ", 1, &record_cmdlist);
+
+  add_cmd ("begin", class_obscure, cmd_record_goto_begin,
+          _("Go to the beginning of the execution log."),
+          &record_goto_cmdlist);
+  add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist);
+
+  add_cmd ("end", class_obscure, cmd_record_goto_end,
+          _("Go to the end of the execution log."),
+          &record_goto_cmdlist);
+
+  add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\
+Print disassembled instructions stored in the execution log.\n\
+With a /m or /s modifier, source lines are included (if available).\n\
+With a /r modifier, raw instructions in hex are included.\n\
+With a /f modifier, function names are omitted.\n\
+With a /p modifier, current position markers are omitted.\n\
+With no argument, disassembles ten more instructions after the previous \
+disassembly.\n\
+\"record instruction-history -\" disassembles ten instructions before a \
+previous disassembly.\n\
+One argument specifies an instruction number as shown by 'info record', and \
+ten instructions are disassembled after that instruction.\n\
+Two arguments with comma between them specify starting and ending instruction \
+numbers to disassemble.\n\
+If the second argument is preceded by '+' or '-', it specifies the distance \
+from the first argument.\n\
+The number of instructions to disassemble can be defined with \"set record \
+instruction-history-size\"."),
+           &record_cmdlist);
+
+  add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\
+Prints the execution history at function granularity.\n\
+It prints one line for each sequence of instructions that belong to the same \
+function.\n\
+Without modifiers, it prints the function name.\n\
+With a /l modifier, the source file and line number range is included.\n\
+With a /i modifier, the instruction number range is included.\n\
+With a /c modifier, the output is indented based on the call stack depth.\n\
+With no argument, prints ten more lines after the previous ten-line print.\n\
+\"record function-call-history -\" prints ten lines before a previous ten-line \
+print.\n\
+One argument specifies a function number as shown by 'info record', and \
+ten lines are printed after that function.\n\
+Two arguments with comma between them specify a range of functions to print.\n\
+If the second argument is preceded by '+' or '-', it specifies the distance \
+from the first argument.\n\
+The number of functions to print can be defined with \"set record \
+function-call-history-size\"."),
+           &record_cmdlist);
+
+  /* Sync command control variables.  */
+  record_insn_history_size_setshow_var = record_insn_history_size;
+  record_call_history_size_setshow_var = record_call_history_size;
 }