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. */ 33unsigned int record_debug = 0; 34 35/* The number of instructions to print in "record instruction-history". */ 36static 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. */ 41static unsigned int record_insn_history_size_setshow_var; 42 43/* The number of functions to print in "record function-call-history". */ 44static 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. */ 48static unsigned int record_call_history_size_setshow_var; 49 50struct cmd_list_element *record_cmdlist = NULL; 51struct cmd_list_element *record_goto_cmdlist = NULL; 52struct cmd_list_element *set_record_cmdlist = NULL; 53struct cmd_list_element *show_record_cmdlist = NULL; 54struct 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 62struct target_ops * 63find_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 70static struct target_ops * 71require_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 85void 86record_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 96void 97record_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 130void 131record_stop (int from_tty) 132{ 133 execute_command_to_string ("record stop", from_tty, false); 134} 135 136/* See record.h. */ 137 138int 139record_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 154static void 155record_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 164static void 165record_unpush (struct target_ops *t) 166{ 167 DEBUG ("unpush %s", t->shortname ()); 168 169 unpush_target (t); 170} 171 172/* See record.h. */ 173 174void 175record_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 189void 190record_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 204void 205record_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 220void 221record_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 236int 237record_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 256static void 257show_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 266static void 267cmd_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 275static void 276cmd_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 301static void 302cmd_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 320static void 321info_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 338static void 339cmd_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 361void 362record_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 380static void 381cmd_record_goto (const char *arg, int from_tty) 382{ 383 record_goto (arg); 384} 385 386/* The "record goto begin" command. */ 387 388static void 389cmd_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 400static void 401cmd_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 412static ULONGEST 413get_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 433static int 434get_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 451static void 452no_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 460static gdb_disassembly_flags 461get_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 525static int 526command_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 538static void 539cmd_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 601static record_print_flags 602get_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 652static void 653cmd_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 718static void 719validate_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 738static void 739set_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 750static void 751set_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 758void _initialize_record (); 759void 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, _("\ 774Set number of instructions to print in \"record instruction-history\"."), _("\ 775Show number of instructions to print in \"record instruction-history\"."), _("\ 776A 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, _("\ 782Set number of function to print in \"record function-call-history\"."), _("\ 783Show number of functions to print in \"record function-call-history\"."), _("\ 784A 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\ 809Usage: record save [FILENAME]\n\ 810Default 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, _("\ 826Restore the program to its state at instruction number N.\n\ 827Argument 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, _("\ 840Print disassembled instructions stored in the execution log.\n\ 841With a /m or /s modifier, source lines are included (if available).\n\ 842With a /r modifier, raw instructions in hex are included.\n\ 843With a /f modifier, function names are omitted.\n\ 844With a /p modifier, current position markers are omitted.\n\ 845With no argument, disassembles ten more instructions after the previous \ 846disassembly.\n\ 847\"record instruction-history -\" disassembles ten instructions before a \ 848previous disassembly.\n\ 849One argument specifies an instruction number as shown by 'info record', and \ 850ten instructions are disassembled after that instruction.\n\ 851Two arguments with comma between them specify starting and ending instruction \ 852numbers to disassemble.\n\ 853If the second argument is preceded by '+' or '-', it specifies the distance \ 854from the first argument.\n\ 855The number of instructions to disassemble can be defined with \"set record \ 856instruction-history-size\"."), 857 &record_cmdlist); 858 859 add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\ 860Prints the execution history at function granularity.\n\ 861It prints one line for each sequence of instructions that belong to the same \ 862function.\n\ 863Without modifiers, it prints the function name.\n\ 864With a /l modifier, the source file and line number range is included.\n\ 865With a /i modifier, the instruction number range is included.\n\ 866With a /c modifier, the output is indented based on the call stack depth.\n\ 867With 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 \ 869print.\n\ 870One argument specifies a function number as shown by 'info record', and \ 871ten lines are printed after that function.\n\ 872Two arguments with comma between them specify a range of functions to print.\n\ 873If the second argument is preceded by '+' or '-', it specifies the distance \ 874from the first argument.\n\ 875The number of functions to print can be defined with \"set record \ 876function-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