1 /* Process record and replay target for GDB, the GNU debugger. 2 3 Copyright (C) 2008-2020 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "defs.h" 21 #include "gdbcmd.h" 22 #include "completer.h" 23 #include "record.h" 24 #include "observable.h" 25 #include "inferior.h" 26 #include "gdbsupport/common-utils.h" 27 #include "cli/cli-utils.h" 28 #include "disasm.h" 29 30 #include <ctype.h> 31 32 /* This is the debug switch for process record. */ 33 unsigned int record_debug = 0; 34 35 /* The number of instructions to print in "record instruction-history". */ 36 static unsigned int record_insn_history_size = 10; 37 38 /* The variable registered as control variable in the "record 39 instruction-history" command. Necessary for extra input 40 validation. */ 41 static unsigned int record_insn_history_size_setshow_var; 42 43 /* The number of functions to print in "record function-call-history". */ 44 static unsigned int record_call_history_size = 10; 45 46 /* The variable registered as control variable in the "record 47 call-history" command. Necessary for extra input validation. */ 48 static unsigned int record_call_history_size_setshow_var; 49 50 struct cmd_list_element *record_cmdlist = NULL; 51 struct cmd_list_element *record_goto_cmdlist = NULL; 52 struct cmd_list_element *set_record_cmdlist = NULL; 53 struct cmd_list_element *show_record_cmdlist = NULL; 54 struct cmd_list_element *info_record_cmdlist = NULL; 55 56 #define DEBUG(msg, args...) \ 57 if (record_debug) \ 58 fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args) 59 60 /* See record.h. */ 61 62 struct target_ops * 63 find_record_target (void) 64 { 65 return find_target_at (record_stratum); 66 } 67 68 /* Check that recording is active. Throw an error, if it isn't. */ 69 70 static struct target_ops * 71 require_record_target (void) 72 { 73 struct target_ops *t; 74 75 t = find_record_target (); 76 if (t == NULL) 77 error (_("No record target is currently active.\n" 78 "Use one of the \"target record-<TAB><TAB>\" commands first.")); 79 80 return t; 81 } 82 83 /* See record.h. */ 84 85 void 86 record_preopen (void) 87 { 88 /* Check if a record target is already running. */ 89 if (find_record_target () != NULL) 90 error (_("The process is already being recorded. Use \"record stop\" to " 91 "stop recording first.")); 92 } 93 94 /* See record.h. */ 95 96 void 97 record_start (const char *method, const char *format, int from_tty) 98 { 99 if (method == NULL) 100 { 101 if (format == NULL) 102 execute_command_to_string ("record", from_tty, false); 103 else 104 error (_("Invalid format.")); 105 } 106 else if (strcmp (method, "full") == 0) 107 { 108 if (format == NULL) 109 execute_command_to_string ("record full", from_tty, false); 110 else 111 error (_("Invalid format.")); 112 } 113 else if (strcmp (method, "btrace") == 0) 114 { 115 if (format == NULL) 116 execute_command_to_string ("record btrace", from_tty, false); 117 else if (strcmp (format, "bts") == 0) 118 execute_command_to_string ("record btrace bts", from_tty, false); 119 else if (strcmp (format, "pt") == 0) 120 execute_command_to_string ("record btrace pt", from_tty, false); 121 else 122 error (_("Invalid format.")); 123 } 124 else 125 error (_("Invalid method.")); 126 } 127 128 /* See record.h. */ 129 130 void 131 record_stop (int from_tty) 132 { 133 execute_command_to_string ("record stop", from_tty, false); 134 } 135 136 /* See record.h. */ 137 138 int 139 record_read_memory (struct gdbarch *gdbarch, 140 CORE_ADDR memaddr, gdb_byte *myaddr, 141 ssize_t len) 142 { 143 int ret = target_read_memory (memaddr, myaddr, len); 144 145 if (ret != 0) 146 DEBUG ("error reading memory at addr %s len = %ld.\n", 147 paddress (gdbarch, memaddr), (long) len); 148 149 return ret; 150 } 151 152 /* Stop recording. */ 153 154 static void 155 record_stop (struct target_ops *t) 156 { 157 DEBUG ("stop %s", t->shortname ()); 158 159 t->stop_recording (); 160 } 161 162 /* Unpush the record target. */ 163 164 static void 165 record_unpush (struct target_ops *t) 166 { 167 DEBUG ("unpush %s", t->shortname ()); 168 169 unpush_target (t); 170 } 171 172 /* See record.h. */ 173 174 void 175 record_disconnect (struct target_ops *t, const char *args, int from_tty) 176 { 177 gdb_assert (t->stratum () == record_stratum); 178 179 DEBUG ("disconnect %s", t->shortname ()); 180 181 record_stop (t); 182 record_unpush (t); 183 184 target_disconnect (args, from_tty); 185 } 186 187 /* See record.h. */ 188 189 void 190 record_detach (struct target_ops *t, inferior *inf, int from_tty) 191 { 192 gdb_assert (t->stratum () == record_stratum); 193 194 DEBUG ("detach %s", t->shortname ()); 195 196 record_stop (t); 197 record_unpush (t); 198 199 target_detach (inf, from_tty); 200 } 201 202 /* See record.h. */ 203 204 void 205 record_mourn_inferior (struct target_ops *t) 206 { 207 gdb_assert (t->stratum () == record_stratum); 208 209 DEBUG ("mourn inferior %s", t->shortname ()); 210 211 /* It is safer to not stop recording. Resources will be freed when 212 threads are discarded. */ 213 record_unpush (t); 214 215 target_mourn_inferior (inferior_ptid); 216 } 217 218 /* See record.h. */ 219 220 void 221 record_kill (struct target_ops *t) 222 { 223 gdb_assert (t->stratum () == record_stratum); 224 225 DEBUG ("kill %s", t->shortname ()); 226 227 /* It is safer to not stop recording. Resources will be freed when 228 threads are discarded. */ 229 record_unpush (t); 230 231 target_kill (); 232 } 233 234 /* See record.h. */ 235 236 int 237 record_check_stopped_by_breakpoint (const address_space *aspace, 238 CORE_ADDR pc, 239 enum target_stop_reason *reason) 240 { 241 if (breakpoint_inserted_here_p (aspace, pc)) 242 { 243 if (hardware_breakpoint_inserted_here_p (aspace, pc)) 244 *reason = TARGET_STOPPED_BY_HW_BREAKPOINT; 245 else 246 *reason = TARGET_STOPPED_BY_SW_BREAKPOINT; 247 return 1; 248 } 249 250 *reason = TARGET_STOPPED_BY_NO_REASON; 251 return 0; 252 } 253 254 /* Implement "show record debug" command. */ 255 256 static void 257 show_record_debug (struct ui_file *file, int from_tty, 258 struct cmd_list_element *c, const char *value) 259 { 260 fprintf_filtered (file, _("Debugging of process record target is %s.\n"), 261 value); 262 } 263 264 /* Alias for "target record". */ 265 266 static void 267 cmd_record_start (const char *args, int from_tty) 268 { 269 execute_command ("target record-full", from_tty); 270 } 271 272 /* Truncate the record log from the present point 273 of replay until the end. */ 274 275 static void 276 cmd_record_delete (const char *args, int from_tty) 277 { 278 require_record_target (); 279 280 if (!target_record_is_replaying (inferior_ptid)) 281 { 282 printf_unfiltered (_("Already at end of record list.\n")); 283 return; 284 } 285 286 if (!target_supports_delete_record ()) 287 { 288 printf_unfiltered (_("The current record target does not support " 289 "this operation.\n")); 290 return; 291 } 292 293 if (!from_tty || query (_("Delete the log from this point forward " 294 "and begin to record the running message " 295 "at current PC?"))) 296 target_delete_record (); 297 } 298 299 /* Implement the "stoprecord" or "record stop" command. */ 300 301 static void 302 cmd_record_stop (const char *args, int from_tty) 303 { 304 struct target_ops *t; 305 306 t = require_record_target (); 307 308 record_stop (t); 309 record_unpush (t); 310 311 printf_unfiltered (_("Process record is stopped and all execution " 312 "logs are deleted.\n")); 313 314 gdb::observers::record_changed.notify (current_inferior (), 0, NULL, NULL); 315 } 316 317 318 /* The "info record" command. */ 319 320 static void 321 info_record_command (const char *args, int from_tty) 322 { 323 struct target_ops *t; 324 325 t = find_record_target (); 326 if (t == NULL) 327 { 328 printf_filtered (_("No record target is currently active.\n")); 329 return; 330 } 331 332 printf_filtered (_("Active record target: %s\n"), t->shortname ()); 333 t->info_record (); 334 } 335 336 /* The "record save" command. */ 337 338 static void 339 cmd_record_save (const char *args, int from_tty) 340 { 341 const char *recfilename; 342 char recfilename_buffer[40]; 343 344 require_record_target (); 345 346 if (args != NULL && *args != 0) 347 recfilename = args; 348 else 349 { 350 /* Default recfile name is "gdb_record.PID". */ 351 xsnprintf (recfilename_buffer, sizeof (recfilename_buffer), 352 "gdb_record.%d", inferior_ptid.pid ()); 353 recfilename = recfilename_buffer; 354 } 355 356 target_save_record (recfilename); 357 } 358 359 /* See record.h. */ 360 361 void 362 record_goto (const char *arg) 363 { 364 ULONGEST insn; 365 366 if (arg == NULL || *arg == '\0') 367 error (_("Command requires an argument (insn number to go to).")); 368 369 insn = parse_and_eval_long (arg); 370 371 require_record_target (); 372 target_goto_record (insn); 373 } 374 375 /* "record goto" command. Argument is an instruction number, 376 as given by "info record". 377 378 Rewinds the recording (forward or backward) to the given instruction. */ 379 380 static void 381 cmd_record_goto (const char *arg, int from_tty) 382 { 383 record_goto (arg); 384 } 385 386 /* The "record goto begin" command. */ 387 388 static void 389 cmd_record_goto_begin (const char *arg, int from_tty) 390 { 391 if (arg != NULL && *arg != '\0') 392 error (_("Junk after argument: %s."), arg); 393 394 require_record_target (); 395 target_goto_record_begin (); 396 } 397 398 /* The "record goto end" command. */ 399 400 static void 401 cmd_record_goto_end (const char *arg, int from_tty) 402 { 403 if (arg != NULL && *arg != '\0') 404 error (_("Junk after argument: %s."), arg); 405 406 require_record_target (); 407 target_goto_record_end (); 408 } 409 410 /* Read an instruction number from an argument string. */ 411 412 static ULONGEST 413 get_insn_number (const char **arg) 414 { 415 ULONGEST number; 416 const char *begin, *end, *pos; 417 418 begin = *arg; 419 pos = skip_spaces (begin); 420 421 if (!isdigit (*pos)) 422 error (_("Expected positive number, got: %s."), pos); 423 424 number = strtoulst (pos, &end, 10); 425 426 *arg += (end - begin); 427 428 return number; 429 } 430 431 /* Read a context size from an argument string. */ 432 433 static int 434 get_context_size (const char **arg) 435 { 436 const char *pos; 437 char *end; 438 439 pos = skip_spaces (*arg); 440 441 if (!isdigit (*pos)) 442 error (_("Expected positive number, got: %s."), pos); 443 444 long result = strtol (pos, &end, 10); 445 *arg = end; 446 return result; 447 } 448 449 /* Complain about junk at the end of an argument string. */ 450 451 static void 452 no_chunk (const char *arg) 453 { 454 if (*arg != 0) 455 error (_("Junk after argument: %s."), arg); 456 } 457 458 /* Read instruction-history modifiers from an argument string. */ 459 460 static gdb_disassembly_flags 461 get_insn_history_modifiers (const char **arg) 462 { 463 gdb_disassembly_flags modifiers; 464 const char *args; 465 466 modifiers = 0; 467 args = *arg; 468 469 if (args == NULL) 470 return modifiers; 471 472 while (*args == '/') 473 { 474 ++args; 475 476 if (*args == '\0') 477 error (_("Missing modifier.")); 478 479 for (; *args; ++args) 480 { 481 if (isspace (*args)) 482 break; 483 484 if (*args == '/') 485 continue; 486 487 switch (*args) 488 { 489 case 'm': 490 case 's': 491 modifiers |= DISASSEMBLY_SOURCE; 492 modifiers |= DISASSEMBLY_FILENAME; 493 break; 494 case 'r': 495 modifiers |= DISASSEMBLY_RAW_INSN; 496 break; 497 case 'f': 498 modifiers |= DISASSEMBLY_OMIT_FNAME; 499 break; 500 case 'p': 501 modifiers |= DISASSEMBLY_OMIT_PC; 502 break; 503 default: 504 error (_("Invalid modifier: %c."), *args); 505 } 506 } 507 508 args = skip_spaces (args); 509 } 510 511 /* Update the argument string. */ 512 *arg = args; 513 514 return modifiers; 515 } 516 517 /* The "set record instruction-history-size / set record 518 function-call-history-size" commands are unsigned, with UINT_MAX 519 meaning unlimited. The target interfaces works with signed int 520 though, to indicate direction, so map "unlimited" to INT_MAX, which 521 is about the same as unlimited in practice. If the user does have 522 a log that huge, she can fetch it in chunks across several requests, 523 but she'll likely have other problems first... */ 524 525 static int 526 command_size_to_target_size (unsigned int size) 527 { 528 gdb_assert (size <= INT_MAX || size == UINT_MAX); 529 530 if (size == UINT_MAX) 531 return INT_MAX; 532 else 533 return size; 534 } 535 536 /* The "record instruction-history" command. */ 537 538 static void 539 cmd_record_insn_history (const char *arg, int from_tty) 540 { 541 require_record_target (); 542 543 gdb_disassembly_flags flags = get_insn_history_modifiers (&arg); 544 545 int size = command_size_to_target_size (record_insn_history_size); 546 547 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 548 target_insn_history (size, flags); 549 else if (strcmp (arg, "-") == 0) 550 target_insn_history (-size, flags); 551 else 552 { 553 ULONGEST begin, end; 554 555 begin = get_insn_number (&arg); 556 557 if (*arg == ',') 558 { 559 arg = skip_spaces (++arg); 560 561 if (*arg == '+') 562 { 563 arg += 1; 564 size = get_context_size (&arg); 565 566 no_chunk (arg); 567 568 target_insn_history_from (begin, size, flags); 569 } 570 else if (*arg == '-') 571 { 572 arg += 1; 573 size = get_context_size (&arg); 574 575 no_chunk (arg); 576 577 target_insn_history_from (begin, -size, flags); 578 } 579 else 580 { 581 end = get_insn_number (&arg); 582 583 no_chunk (arg); 584 585 target_insn_history_range (begin, end, flags); 586 } 587 } 588 else 589 { 590 no_chunk (arg); 591 592 target_insn_history_from (begin, size, flags); 593 } 594 595 dont_repeat (); 596 } 597 } 598 599 /* Read function-call-history modifiers from an argument string. */ 600 601 static record_print_flags 602 get_call_history_modifiers (const char **arg) 603 { 604 record_print_flags modifiers = 0; 605 const char *args = *arg; 606 607 if (args == NULL) 608 return modifiers; 609 610 while (*args == '/') 611 { 612 ++args; 613 614 if (*args == '\0') 615 error (_("Missing modifier.")); 616 617 for (; *args; ++args) 618 { 619 if (isspace (*args)) 620 break; 621 622 if (*args == '/') 623 continue; 624 625 switch (*args) 626 { 627 case 'l': 628 modifiers |= RECORD_PRINT_SRC_LINE; 629 break; 630 case 'i': 631 modifiers |= RECORD_PRINT_INSN_RANGE; 632 break; 633 case 'c': 634 modifiers |= RECORD_PRINT_INDENT_CALLS; 635 break; 636 default: 637 error (_("Invalid modifier: %c."), *args); 638 } 639 } 640 641 args = skip_spaces (args); 642 } 643 644 /* Update the argument string. */ 645 *arg = args; 646 647 return modifiers; 648 } 649 650 /* The "record function-call-history" command. */ 651 652 static void 653 cmd_record_call_history (const char *arg, int from_tty) 654 { 655 require_record_target (); 656 657 record_print_flags flags = get_call_history_modifiers (&arg); 658 659 int size = command_size_to_target_size (record_call_history_size); 660 661 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 662 target_call_history (size, flags); 663 else if (strcmp (arg, "-") == 0) 664 target_call_history (-size, flags); 665 else 666 { 667 ULONGEST begin, end; 668 669 begin = get_insn_number (&arg); 670 671 if (*arg == ',') 672 { 673 arg = skip_spaces (++arg); 674 675 if (*arg == '+') 676 { 677 arg += 1; 678 size = get_context_size (&arg); 679 680 no_chunk (arg); 681 682 target_call_history_from (begin, size, flags); 683 } 684 else if (*arg == '-') 685 { 686 arg += 1; 687 size = get_context_size (&arg); 688 689 no_chunk (arg); 690 691 target_call_history_from (begin, -size, flags); 692 } 693 else 694 { 695 end = get_insn_number (&arg); 696 697 no_chunk (arg); 698 699 target_call_history_range (begin, end, flags); 700 } 701 } 702 else 703 { 704 no_chunk (arg); 705 706 target_call_history_from (begin, size, flags); 707 } 708 709 dont_repeat (); 710 } 711 } 712 713 /* Helper for "set record instruction-history-size" and "set record 714 function-call-history-size" input validation. COMMAND_VAR is the 715 variable registered in the command as control variable. *SETTING 716 is the real setting the command allows changing. */ 717 718 static void 719 validate_history_size (unsigned int *command_var, unsigned int *setting) 720 { 721 if (*command_var != UINT_MAX && *command_var > INT_MAX) 722 { 723 unsigned int new_value = *command_var; 724 725 /* Restore previous value. */ 726 *command_var = *setting; 727 error (_("integer %u out of range"), new_value); 728 } 729 730 /* Commit new value. */ 731 *setting = *command_var; 732 } 733 734 /* Called by do_setshow_command. We only want values in the 735 [0..INT_MAX] range, while the command's machinery accepts 736 [0..UINT_MAX]. See command_size_to_target_size. */ 737 738 static void 739 set_record_insn_history_size (const char *args, int from_tty, 740 struct cmd_list_element *c) 741 { 742 validate_history_size (&record_insn_history_size_setshow_var, 743 &record_insn_history_size); 744 } 745 746 /* Called by do_setshow_command. We only want values in the 747 [0..INT_MAX] range, while the command's machinery accepts 748 [0..UINT_MAX]. See command_size_to_target_size. */ 749 750 static void 751 set_record_call_history_size (const char *args, int from_tty, 752 struct cmd_list_element *c) 753 { 754 validate_history_size (&record_call_history_size_setshow_var, 755 &record_call_history_size); 756 } 757 758 void _initialize_record (); 759 void 760 _initialize_record () 761 { 762 struct cmd_list_element *c; 763 764 add_setshow_zuinteger_cmd ("record", no_class, &record_debug, 765 _("Set debugging of record/replay feature."), 766 _("Show debugging of record/replay feature."), 767 _("When enabled, debugging output for " 768 "record/replay feature is displayed."), 769 NULL, show_record_debug, &setdebuglist, 770 &showdebuglist); 771 772 add_setshow_uinteger_cmd ("instruction-history-size", no_class, 773 &record_insn_history_size_setshow_var, _("\ 774 Set number of instructions to print in \"record instruction-history\"."), _("\ 775 Show number of instructions to print in \"record instruction-history\"."), _("\ 776 A size of \"unlimited\" means unlimited instructions. The default is 10."), 777 set_record_insn_history_size, NULL, 778 &set_record_cmdlist, &show_record_cmdlist); 779 780 add_setshow_uinteger_cmd ("function-call-history-size", no_class, 781 &record_call_history_size_setshow_var, _("\ 782 Set number of function to print in \"record function-call-history\"."), _("\ 783 Show number of functions to print in \"record function-call-history\"."), _("\ 784 A size of \"unlimited\" means unlimited lines. The default is 10."), 785 set_record_call_history_size, NULL, 786 &set_record_cmdlist, &show_record_cmdlist); 787 788 c = add_prefix_cmd ("record", class_obscure, cmd_record_start, 789 _("Start recording."), 790 &record_cmdlist, "record ", 0, &cmdlist); 791 set_cmd_completer (c, filename_completer); 792 793 add_com_alias ("rec", "record", class_obscure, 1); 794 add_basic_prefix_cmd ("record", class_support, 795 _("Set record options."), &set_record_cmdlist, 796 "set record ", 0, &setlist); 797 add_alias_cmd ("rec", "record", class_obscure, 1, &setlist); 798 add_show_prefix_cmd ("record", class_support, 799 _("Show record options."), &show_record_cmdlist, 800 "show record ", 0, &showlist); 801 add_alias_cmd ("rec", "record", class_obscure, 1, &showlist); 802 add_prefix_cmd ("record", class_support, info_record_command, 803 _("Info record options."), &info_record_cmdlist, 804 "info record ", 0, &infolist); 805 add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); 806 807 c = add_cmd ("save", class_obscure, cmd_record_save, 808 _("Save the execution log to a file.\n\ 809 Usage: record save [FILENAME]\n\ 810 Default filename is 'gdb_record.PROCESS_ID'."), 811 &record_cmdlist); 812 set_cmd_completer (c, filename_completer); 813 814 add_cmd ("delete", class_obscure, cmd_record_delete, 815 _("Delete the rest of execution log and start recording it anew."), 816 &record_cmdlist); 817 add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist); 818 add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist); 819 820 add_cmd ("stop", class_obscure, cmd_record_stop, 821 _("Stop the record/replay target."), 822 &record_cmdlist); 823 add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); 824 825 add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\ 826 Restore the program to its state at instruction number N.\n\ 827 Argument is instruction number, as shown by 'info record'."), 828 &record_goto_cmdlist, "record goto ", 1, &record_cmdlist); 829 830 add_cmd ("begin", class_obscure, cmd_record_goto_begin, 831 _("Go to the beginning of the execution log."), 832 &record_goto_cmdlist); 833 add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist); 834 835 add_cmd ("end", class_obscure, cmd_record_goto_end, 836 _("Go to the end of the execution log."), 837 &record_goto_cmdlist); 838 839 add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\ 840 Print disassembled instructions stored in the execution log.\n\ 841 With a /m or /s modifier, source lines are included (if available).\n\ 842 With a /r modifier, raw instructions in hex are included.\n\ 843 With a /f modifier, function names are omitted.\n\ 844 With a /p modifier, current position markers are omitted.\n\ 845 With no argument, disassembles ten more instructions after the previous \ 846 disassembly.\n\ 847 \"record instruction-history -\" disassembles ten instructions before a \ 848 previous disassembly.\n\ 849 One argument specifies an instruction number as shown by 'info record', and \ 850 ten instructions are disassembled after that instruction.\n\ 851 Two arguments with comma between them specify starting and ending instruction \ 852 numbers to disassemble.\n\ 853 If the second argument is preceded by '+' or '-', it specifies the distance \ 854 from the first argument.\n\ 855 The number of instructions to disassemble can be defined with \"set record \ 856 instruction-history-size\"."), 857 &record_cmdlist); 858 859 add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\ 860 Prints the execution history at function granularity.\n\ 861 It prints one line for each sequence of instructions that belong to the same \ 862 function.\n\ 863 Without modifiers, it prints the function name.\n\ 864 With a /l modifier, the source file and line number range is included.\n\ 865 With a /i modifier, the instruction number range is included.\n\ 866 With a /c modifier, the output is indented based on the call stack depth.\n\ 867 With no argument, prints ten more lines after the previous ten-line print.\n\ 868 \"record function-call-history -\" prints ten lines before a previous ten-line \ 869 print.\n\ 870 One argument specifies a function number as shown by 'info record', and \ 871 ten lines are printed after that function.\n\ 872 Two arguments with comma between them specify a range of functions to print.\n\ 873 If the second argument is preceded by '+' or '-', it specifies the distance \ 874 from the first argument.\n\ 875 The number of functions to print can be defined with \"set record \ 876 function-call-history-size\"."), 877 &record_cmdlist); 878 879 /* Sync command control variables. */ 880 record_insn_history_size_setshow_var = record_insn_history_size; 881 record_call_history_size_setshow_var = record_call_history_size; 882 } 883