gdb/record.c - gdb

Global variables defined

Functions defined

Macros defined

Source code

  1. /* Process record and replay target for GDB, the GNU debugger.

  2.    Copyright (C) 2008-2015 Free Software Foundation, Inc.

  3.    This file is part of GDB.

  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 3 of the License, or
  7.    (at your option) any later version.

  8.    This program is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.    GNU General Public License for more details.

  12.    You should have received a copy of the GNU General Public License
  13.    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

  14. #include "defs.h"
  15. #include "gdbcmd.h"
  16. #include "completer.h"
  17. #include "record.h"
  18. #include "observer.h"
  19. #include "inferior.h"
  20. #include "common/common-utils.h"
  21. #include "cli/cli-utils.h"
  22. #include "disasm.h"

  23. #include <ctype.h>

  24. /* This is the debug switch for process record.  */
  25. unsigned int record_debug = 0;

  26. /* The number of instructions to print in "record instruction-history".  */
  27. static unsigned int record_insn_history_size = 10;

  28. /* The variable registered as control variable in the "record
  29.    instruction-history" command.  Necessary for extra input
  30.    validation.  */
  31. static unsigned int record_insn_history_size_setshow_var;

  32. /* The number of functions to print in "record function-call-history".  */
  33. static unsigned int record_call_history_size = 10;

  34. /* The variable registered as control variable in the "record
  35.    call-history" command.  Necessary for extra input validation.  */
  36. static unsigned int record_call_history_size_setshow_var;

  37. struct cmd_list_element *record_cmdlist = NULL;
  38. struct cmd_list_element *record_goto_cmdlist = NULL;
  39. struct cmd_list_element *set_record_cmdlist = NULL;
  40. struct cmd_list_element *show_record_cmdlist = NULL;
  41. struct cmd_list_element *info_record_cmdlist = NULL;

  42. #define DEBUG(msg, args...)                                                \
  43.   if (record_debug)                                                        \
  44.     fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)

  45. /* See record.h.  */

  46. struct target_ops *
  47. find_record_target (void)
  48. {
  49.   return find_target_at (record_stratum);
  50. }

  51. /* Check that recording is active.  Throw an error, if it isn't.  */

  52. static struct target_ops *
  53. require_record_target (void)
  54. {
  55.   struct target_ops *t;

  56.   t = find_record_target ();
  57.   if (t == NULL)
  58.     error (_("No record target is currently active.\n"
  59.              "Use one of the \"target record-<tab><tab>\" commands first."));

  60.   return t;
  61. }

  62. /* See record.h.  */

  63. void
  64. record_preopen (void)
  65. {
  66.   /* Check if a record target is already running.  */
  67.   if (find_record_target () != NULL)
  68.     error (_("The process is already being recorded.  Use \"record stop\" to "
  69.              "stop recording first."));
  70. }

  71. /* See record.h.  */

  72. int
  73. record_read_memory (struct gdbarch *gdbarch,
  74.                     CORE_ADDR memaddr, gdb_byte *myaddr,
  75.                     ssize_t len)
  76. {
  77.   int ret = target_read_memory (memaddr, myaddr, len);

  78.   if (ret != 0)
  79.     DEBUG ("error reading memory at addr %s len = %ld.\n",
  80.            paddress (gdbarch, memaddr), (long) len);

  81.   return ret;
  82. }

  83. /* Stop recording.  */

  84. static void
  85. record_stop (struct target_ops *t)
  86. {
  87.   DEBUG ("stop %s", t->to_shortname);

  88.   t->to_stop_recording (t);
  89. }

  90. /* Unpush the record target.  */

  91. static void
  92. record_unpush (struct target_ops *t)
  93. {
  94.   DEBUG ("unpush %s", t->to_shortname);

  95.   unpush_target (t);
  96. }

  97. /* See record.h.  */

  98. void
  99. record_disconnect (struct target_ops *t, const char *args, int from_tty)
  100. {
  101.   gdb_assert (t->to_stratum == record_stratum);

  102.   DEBUG ("disconnect %s", t->to_shortname);

  103.   record_stop (t);
  104.   record_unpush (t);

  105.   target_disconnect (args, from_tty);
  106. }

  107. /* See record.h.  */

  108. void
  109. record_detach (struct target_ops *t, const char *args, int from_tty)
  110. {
  111.   gdb_assert (t->to_stratum == record_stratum);

  112.   DEBUG ("detach %s", t->to_shortname);

  113.   record_stop (t);
  114.   record_unpush (t);

  115.   target_detach (args, from_tty);
  116. }

  117. /* See record.h.  */

  118. void
  119. record_mourn_inferior (struct target_ops *t)
  120. {
  121.   gdb_assert (t->to_stratum == record_stratum);

  122.   DEBUG ("mourn inferior %s", t->to_shortname);

  123.   /* It is safer to not stop recording.  Resources will be freed when
  124.      threads are discarded.  */
  125.   record_unpush (t);

  126.   target_mourn_inferior ();
  127. }

  128. /* See record.h.  */

  129. void
  130. record_kill (struct target_ops *t)
  131. {
  132.   gdb_assert (t->to_stratum == record_stratum);

  133.   DEBUG ("kill %s", t->to_shortname);

  134.   /* It is safer to not stop recording.  Resources will be freed when
  135.      threads are discarded.  */
  136.   record_unpush (t);

  137.   target_kill ();
  138. }

  139. /* Implement "show record debug" command.  */

  140. static void
  141. show_record_debug (struct ui_file *file, int from_tty,
  142.                    struct cmd_list_element *c, const char *value)
  143. {
  144.   fprintf_filtered (file, _("Debugging of process record target is %s.\n"),
  145.                     value);
  146. }

  147. /* Alias for "target record".  */

  148. static void
  149. cmd_record_start (char *args, int from_tty)
  150. {
  151.   execute_command ("target record-full", from_tty);
  152. }

  153. /* Truncate the record log from the present point
  154.    of replay until the end.  */

  155. static void
  156. cmd_record_delete (char *args, int from_tty)
  157. {
  158.   require_record_target ();

  159.   if (!target_record_is_replaying ())
  160.     {
  161.       printf_unfiltered (_("Already at end of record list.\n"));
  162.       return;
  163.     }

  164.   if (!target_supports_delete_record ())
  165.     {
  166.       printf_unfiltered (_("The current record target does not support "
  167.                            "this operation.\n"));
  168.       return;
  169.     }

  170.   if (!from_tty || query (_("Delete the log from this point forward "
  171.                             "and begin to record the running message "
  172.                             "at current PC?")))
  173.     target_delete_record ();
  174. }

  175. /* Implement the "stoprecord" or "record stop" command.  */

  176. static void
  177. cmd_record_stop (char *args, int from_tty)
  178. {
  179.   struct target_ops *t;

  180.   t = require_record_target ();

  181.   record_stop (t);
  182.   record_unpush (t);

  183.   printf_unfiltered (_("Process record is stopped and all execution "
  184.                        "logs are deleted.\n"));

  185.   observer_notify_record_changed (current_inferior (), 0);
  186. }

  187. /* The "set record" command.  */

  188. static void
  189. set_record_command (char *args, int from_tty)
  190. {
  191.   printf_unfiltered (_("\"set record\" must be followed "
  192.                        "by an apporpriate subcommand.\n"));
  193.   help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
  194. }

  195. /* The "show record" command.  */

  196. static void
  197. show_record_command (char *args, int from_tty)
  198. {
  199.   cmd_show_list (show_record_cmdlist, from_tty, "");
  200. }

  201. /* The "info record" command.  */

  202. static void
  203. info_record_command (char *args, int from_tty)
  204. {
  205.   struct target_ops *t;

  206.   t = find_record_target ();
  207.   if (t == NULL)
  208.     {
  209.       printf_filtered (_("No record target is currently active.\n"));
  210.       return;
  211.     }

  212.   printf_filtered (_("Active record target: %s\n"), t->to_shortname);
  213.   t->to_info_record (t);
  214. }

  215. /* The "record save" command.  */

  216. static void
  217. cmd_record_save (char *args, int from_tty)
  218. {
  219.   char *recfilename, recfilename_buffer[40];

  220.   require_record_target ();

  221.   if (args != NULL && *args != 0)
  222.     recfilename = args;
  223.   else
  224.     {
  225.       /* Default recfile name is "gdb_record.PID".  */
  226.       xsnprintf (recfilename_buffer, sizeof (recfilename_buffer),
  227.                 "gdb_record.%d", ptid_get_pid (inferior_ptid));
  228.       recfilename = recfilename_buffer;
  229.     }

  230.   target_save_record (recfilename);
  231. }

  232. /* See record.h.  */

  233. void
  234. record_goto (const char *arg)
  235. {
  236.   ULONGEST insn;

  237.   if (arg == NULL || *arg == '\0')
  238.     error (_("Command requires an argument (insn number to go to)."));

  239.   insn = parse_and_eval_long (arg);

  240.   require_record_target ();
  241.   target_goto_record (insn);
  242. }

  243. /* "record goto" command.  Argument is an instruction number,
  244.    as given by "info record".

  245.    Rewinds the recording (forward or backward) to the given instruction.  */

  246. static void
  247. cmd_record_goto (char *arg, int from_tty)
  248. {
  249.   record_goto (arg);
  250. }

  251. /* The "record goto begin" command.  */

  252. static void
  253. cmd_record_goto_begin (char *arg, int from_tty)
  254. {
  255.   if (arg != NULL && *arg != '\0')
  256.     error (_("Junk after argument: %s."), arg);

  257.   require_record_target ();
  258.   target_goto_record_begin ();
  259. }

  260. /* The "record goto end" command.  */

  261. static void
  262. cmd_record_goto_end (char *arg, int from_tty)
  263. {
  264.   if (arg != NULL && *arg != '\0')
  265.     error (_("Junk after argument: %s."), arg);

  266.   require_record_target ();
  267.   target_goto_record_end ();
  268. }

  269. /* Read an instruction number from an argument string.  */

  270. static ULONGEST
  271. get_insn_number (char **arg)
  272. {
  273.   ULONGEST number;
  274.   const char *begin, *end, *pos;

  275.   begin = *arg;
  276.   pos = skip_spaces_const (begin);

  277.   if (!isdigit (*pos))
  278.     error (_("Expected positive number, got: %s."), pos);

  279.   number = strtoulst (pos, &end, 10);

  280.   *arg += (end - begin);

  281.   return number;
  282. }

  283. /* Read a context size from an argument string.  */

  284. static int
  285. get_context_size (char **arg)
  286. {
  287.   char *pos;
  288.   int number;

  289.   pos = skip_spaces (*arg);

  290.   if (!isdigit (*pos))
  291.     error (_("Expected positive number, got: %s."), pos);

  292.   return strtol (pos, arg, 10);
  293. }

  294. /* Complain about junk at the end of an argument string.  */

  295. static void
  296. no_chunk (char *arg)
  297. {
  298.   if (*arg != 0)
  299.     error (_("Junk after argument: %s."), arg);
  300. }

  301. /* Read instruction-history modifiers from an argument string.  */

  302. static int
  303. get_insn_history_modifiers (char **arg)
  304. {
  305.   int modifiers;
  306.   char *args;

  307.   modifiers = 0;
  308.   args = *arg;

  309.   if (args == NULL)
  310.     return modifiers;

  311.   while (*args == '/')
  312.     {
  313.       ++args;

  314.       if (*args == '\0')
  315.         error (_("Missing modifier."));

  316.       for (; *args; ++args)
  317.         {
  318.           if (isspace (*args))
  319.             break;

  320.           if (*args == '/')
  321.             continue;

  322.           switch (*args)
  323.             {
  324.             case 'm':
  325.               modifiers |= DISASSEMBLY_SOURCE;
  326.               modifiers |= DISASSEMBLY_FILENAME;
  327.               break;
  328.             case 'r':
  329.               modifiers |= DISASSEMBLY_RAW_INSN;
  330.               break;
  331.             case 'f':
  332.               modifiers |= DISASSEMBLY_OMIT_FNAME;
  333.               break;
  334.             case 'p':
  335.               modifiers |= DISASSEMBLY_OMIT_PC;
  336.               break;
  337.             default:
  338.               error (_("Invalid modifier: %c."), *args);
  339.             }
  340.         }

  341.       args = skip_spaces (args);
  342.     }

  343.   /* Update the argument string.  */
  344.   *arg = args;

  345.   return modifiers;
  346. }

  347. /* The "set record instruction-history-size / set record
  348.    function-call-history-size" commands are unsigned, with UINT_MAX
  349.    meaning unlimited.  The target interfaces works with signed int
  350.    though, to indicate direction, so map "unlimited" to INT_MAX, which
  351.    is about the same as unlimited in practice.  If the user does have
  352.    a log that huge, she can fetch it in chunks across several requests,
  353.    but she'll likely have other problems first...  */

  354. static int
  355. command_size_to_target_size (unsigned int size)
  356. {
  357.   gdb_assert (size <= INT_MAX || size == UINT_MAX);

  358.   if (size == UINT_MAX)
  359.     return INT_MAX;
  360.   else
  361.     return size;
  362. }

  363. /* The "record instruction-history" command.  */

  364. static void
  365. cmd_record_insn_history (char *arg, int from_tty)
  366. {
  367.   int flags, size;

  368.   require_record_target ();

  369.   flags = get_insn_history_modifiers (&arg);

  370.   size = command_size_to_target_size (record_insn_history_size);

  371.   if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
  372.     target_insn_history (size, flags);
  373.   else if (strcmp (arg, "-") == 0)
  374.     target_insn_history (-size, flags);
  375.   else
  376.     {
  377.       ULONGEST begin, end;

  378.       begin = get_insn_number (&arg);

  379.       if (*arg == ',')
  380.         {
  381.           arg = skip_spaces (++arg);

  382.           if (*arg == '+')
  383.             {
  384.               arg += 1;
  385.               size = get_context_size (&arg);

  386.               no_chunk (arg);

  387.               target_insn_history_from (begin, size, flags);
  388.             }
  389.           else if (*arg == '-')
  390.             {
  391.               arg += 1;
  392.               size = get_context_size (&arg);

  393.               no_chunk (arg);

  394.               target_insn_history_from (begin, -size, flags);
  395.             }
  396.           else
  397.             {
  398.               end = get_insn_number (&arg);

  399.               no_chunk (arg);

  400.               target_insn_history_range (begin, end, flags);
  401.             }
  402.         }
  403.       else
  404.         {
  405.           no_chunk (arg);

  406.           target_insn_history_from (begin, size, flags);
  407.         }

  408.       dont_repeat ();
  409.     }
  410. }

  411. /* Read function-call-history modifiers from an argument string.  */

  412. static int
  413. get_call_history_modifiers (char **arg)
  414. {
  415.   int modifiers;
  416.   char *args;

  417.   modifiers = 0;
  418.   args = *arg;

  419.   if (args == NULL)
  420.     return modifiers;

  421.   while (*args == '/')
  422.     {
  423.       ++args;

  424.       if (*args == '\0')
  425.         error (_("Missing modifier."));

  426.       for (; *args; ++args)
  427.         {
  428.           if (isspace (*args))
  429.             break;

  430.           if (*args == '/')
  431.             continue;

  432.           switch (*args)
  433.             {
  434.             case 'l':
  435.               modifiers |= RECORD_PRINT_SRC_LINE;
  436.               break;
  437.             case 'i':
  438.               modifiers |= RECORD_PRINT_INSN_RANGE;
  439.               break;
  440.             case 'c':
  441.               modifiers |= RECORD_PRINT_INDENT_CALLS;
  442.               break;
  443.             default:
  444.               error (_("Invalid modifier: %c."), *args);
  445.             }
  446.         }

  447.       args = skip_spaces (args);
  448.     }

  449.   /* Update the argument string.  */
  450.   *arg = args;

  451.   return modifiers;
  452. }

  453. /* The "record function-call-history" command.  */

  454. static void
  455. cmd_record_call_history (char *arg, int from_tty)
  456. {
  457.   int flags, size;

  458.   require_record_target ();

  459.   flags = get_call_history_modifiers (&arg);

  460.   size = command_size_to_target_size (record_call_history_size);

  461.   if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
  462.     target_call_history (size, flags);
  463.   else if (strcmp (arg, "-") == 0)
  464.     target_call_history (-size, flags);
  465.   else
  466.     {
  467.       ULONGEST begin, end;

  468.       begin = get_insn_number (&arg);

  469.       if (*arg == ',')
  470.         {
  471.           arg = skip_spaces (++arg);

  472.           if (*arg == '+')
  473.             {
  474.               arg += 1;
  475.               size = get_context_size (&arg);

  476.               no_chunk (arg);

  477.               target_call_history_from (begin, size, flags);
  478.             }
  479.           else if (*arg == '-')
  480.             {
  481.               arg += 1;
  482.               size = get_context_size (&arg);

  483.               no_chunk (arg);

  484.               target_call_history_from (begin, -size, flags);
  485.             }
  486.           else
  487.             {
  488.               end = get_insn_number (&arg);

  489.               no_chunk (arg);

  490.               target_call_history_range (begin, end, flags);
  491.             }
  492.         }
  493.       else
  494.         {
  495.           no_chunk (arg);

  496.           target_call_history_from (begin, size, flags);
  497.         }

  498.       dont_repeat ();
  499.     }
  500. }

  501. /* Helper for "set record instruction-history-size" and "set record
  502.    function-call-history-size" input validation.  COMMAND_VAR is the
  503.    variable registered in the command as control variable.  *SETTING
  504.    is the real setting the command allows changing.  */

  505. static void
  506. validate_history_size (unsigned int *command_var, unsigned int *setting)
  507. {
  508.   if (*command_var != UINT_MAX && *command_var > INT_MAX)
  509.     {
  510.       unsigned int new_value = *command_var;

  511.       /* Restore previous value.  */
  512.       *command_var = *setting;
  513.       error (_("integer %u out of range"), new_value);
  514.     }

  515.   /* Commit new value.  */
  516.   *setting = *command_var;
  517. }

  518. /* Called by do_setshow_command.  We only want values in the
  519.    [0..INT_MAX] range, while the command's machinery accepts
  520.    [0..UINT_MAX].  See command_size_to_target_size.  */

  521. static void
  522. set_record_insn_history_size (char *args, int from_tty,
  523.                               struct cmd_list_element *c)
  524. {
  525.   validate_history_size (&record_insn_history_size_setshow_var,
  526.                          &record_insn_history_size);
  527. }

  528. /* Called by do_setshow_command.  We only want values in the
  529.    [0..INT_MAX] range, while the command's machinery accepts
  530.    [0..UINT_MAX].  See command_size_to_target_size.  */

  531. static void
  532. set_record_call_history_size (char *args, int from_tty,
  533.                               struct cmd_list_element *c)
  534. {
  535.   validate_history_size (&record_call_history_size_setshow_var,
  536.                          &record_call_history_size);
  537. }

  538. /* Provide a prototype to silence -Wmissing-prototypes.  */
  539. extern initialize_file_ftype _initialize_record;

  540. void
  541. _initialize_record (void)
  542. {
  543.   struct cmd_list_element *c;

  544.   add_setshow_zuinteger_cmd ("record", no_class, &record_debug,
  545.                              _("Set debugging of record/replay feature."),
  546.                              _("Show debugging of record/replay feature."),
  547.                              _("When enabled, debugging output for "
  548.                                "record/replay feature is displayed."),
  549.                              NULL, show_record_debug, &setdebuglist,
  550.                              &showdebuglist);

  551.   add_setshow_uinteger_cmd ("instruction-history-size", no_class,
  552.                             &record_insn_history_size_setshow_var, _("\
  553. Set number of instructions to print in \"record instruction-history\"."), _("\
  554. Show number of instructions to print in \"record instruction-history\"."), _("\
  555. A size of \"unlimited\" means unlimited instructions.  The default is 10."),
  556.                             set_record_insn_history_size, NULL,
  557.                             &set_record_cmdlist, &show_record_cmdlist);

  558.   add_setshow_uinteger_cmd ("function-call-history-size", no_class,
  559.                             &record_call_history_size_setshow_var, _("\
  560. Set number of function to print in \"record function-call-history\"."), _("\
  561. Show number of functions to print in \"record function-call-history\"."), _("\
  562. A size of \"unlimited\" means unlimited lines.  The default is 10."),
  563.                             set_record_call_history_size, NULL,
  564.                             &set_record_cmdlist, &show_record_cmdlist);

  565.   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
  566.                       _("Start recording."),
  567.                       &record_cmdlist, "record ", 0, &cmdlist);
  568.   set_cmd_completer (c, filename_completer);

  569.   add_com_alias ("rec", "record", class_obscure, 1);
  570.   add_prefix_cmd ("record", class_support, set_record_command,
  571.                   _("Set record options"), &set_record_cmdlist,
  572.                   "set record ", 0, &setlist);
  573.   add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
  574.   add_prefix_cmd ("record", class_support, show_record_command,
  575.                   _("Show record options"), &show_record_cmdlist,
  576.                   "show record ", 0, &showlist);
  577.   add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
  578.   add_prefix_cmd ("record", class_support, info_record_command,
  579.                   _("Info record options"), &info_record_cmdlist,
  580.                   "info record ", 0, &infolist);
  581.   add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);

  582.   c = add_cmd ("save", class_obscure, cmd_record_save,
  583.                _("Save the execution log to a file.\n\
  584. Argument is optional filename.\n\
  585. Default filename is 'gdb_record.<process_id>'."),
  586.                &record_cmdlist);
  587.   set_cmd_completer (c, filename_completer);

  588.   add_cmd ("delete", class_obscure, cmd_record_delete,
  589.            _("Delete the rest of execution log and start recording it anew."),
  590.            &record_cmdlist);
  591.   add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist);
  592.   add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist);

  593.   add_cmd ("stop", class_obscure, cmd_record_stop,
  594.            _("Stop the record/replay target."),
  595.            &record_cmdlist);
  596.   add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);

  597.   add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\
  598. Restore the program to its state at instruction number N.\n\
  599. Argument is instruction number, as shown by 'info record'."),
  600.                   &record_goto_cmdlist, "record goto ", 1, &record_cmdlist);

  601.   add_cmd ("begin", class_obscure, cmd_record_goto_begin,
  602.            _("Go to the beginning of the execution log."),
  603.            &record_goto_cmdlist);
  604.   add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist);

  605.   add_cmd ("end", class_obscure, cmd_record_goto_end,
  606.            _("Go to the end of the execution log."),
  607.            &record_goto_cmdlist);

  608.   add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\
  609. Print disassembled instructions stored in the execution log.\n\
  610. With a /m modifier, source lines are included (if available).\n\
  611. With a /r modifier, raw instructions in hex are included.\n\
  612. With a /f modifier, function names are omitted.\n\
  613. With a /p modifier, current position markers are omitted.\n\
  614. With no argument, disassembles ten more instructions after the previous \
  615. disassembly.\n\
  616. \"record instruction-history -\" disassembles ten instructions before a \
  617. previous disassembly.\n\
  618. One argument specifies an instruction number as shown by 'info record', and \
  619. ten instructions are disassembled after that instruction.\n\
  620. Two arguments with comma between them specify starting and ending instruction \
  621. numbers to disassemble.\n\
  622. If the second argument is preceded by '+' or '-', it specifies the distance \
  623. from the first argument.\n\
  624. The number of instructions to disassemble can be defined with \"set record \
  625. instruction-history-size\"."),
  626.            &record_cmdlist);

  627.   add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\
  628. Prints the execution history at function granularity.\n\
  629. It prints one line for each sequence of instructions that belong to the same \
  630. function.\n\
  631. Without modifiers, it prints the function name.\n\
  632. With a /l modifier, the source file and line number range is included.\n\
  633. With a /i modifier, the instruction number range is included.\n\
  634. With a /c modifier, the output is indented based on the call stack depth.\n\
  635. With no argument, prints ten more lines after the previous ten-line print.\n\
  636. \"record function-call-history -\" prints ten lines before a previous ten-line \
  637. print.\n\
  638. One argument specifies a function number as shown by 'info record', and \
  639. ten lines are printed after that function.\n\
  640. Two arguments with comma between them specify a range of functions to print.\n\
  641. If the second argument is preceded by '+' or '-', it specifies the distance \
  642. from the first argument.\n\
  643. The number of functions to print can be defined with \"set record \
  644. function-call-history-size\"."),
  645.            &record_cmdlist);

  646.   /* Sync command control variables.  */
  647.   record_insn_history_size_setshow_var = record_insn_history_size;
  648.   record_call_history_size_setshow_var = record_call_history_size;
  649. }