1 /* Process record and replay target for GDB, the GNU debugger. 2 3 Copyright (C) 2008-2017 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 "observer.h" 25 #include "inferior.h" 26 #include "common/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 ((char *) "record", from_tty); 103 else 104 error (_("Invalid format.")); 105 } 106 else if (strcmp (method, "full") == 0) 107 { 108 if (format == NULL) 109 execute_command_to_string ((char *) "record full", from_tty); 110 else 111 error (_("Invalid format.")); 112 } 113 else if (strcmp (method, "btrace") == 0) 114 { 115 if (format == NULL) 116 execute_command_to_string ((char *) "record btrace", from_tty); 117 else if (strcmp (format, "bts") == 0) 118 execute_command_to_string ((char *) "record btrace bts", from_tty); 119 else if (strcmp (format, "pt") == 0) 120 execute_command_to_string ((char *) "record btrace pt", from_tty); 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 ((char *) "record stop", from_tty); 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->to_shortname); 158 159 t->to_stop_recording (t); 160 } 161 162 /* Unpush the record target. */ 163 164 static void 165 record_unpush (struct target_ops *t) 166 { 167 DEBUG ("unpush %s", t->to_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->to_stratum == record_stratum); 178 179 DEBUG ("disconnect %s", t->to_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, const char *args, int from_tty) 191 { 192 gdb_assert (t->to_stratum == record_stratum); 193 194 DEBUG ("detach %s", t->to_shortname); 195 196 record_stop (t); 197 record_unpush (t); 198 199 target_detach (args, from_tty); 200 } 201 202 /* See record.h. */ 203 204 void 205 record_mourn_inferior (struct target_ops *t) 206 { 207 gdb_assert (t->to_stratum == record_stratum); 208 209 DEBUG ("mourn inferior %s", t->to_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->to_stratum == record_stratum); 224 225 DEBUG ("kill %s", t->to_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 (struct address_space *aspace, CORE_ADDR pc, 238 enum target_stop_reason *reason) 239 { 240 if (breakpoint_inserted_here_p (aspace, pc)) 241 { 242 if (hardware_breakpoint_inserted_here_p (aspace, pc)) 243 *reason = TARGET_STOPPED_BY_HW_BREAKPOINT; 244 else 245 *reason = TARGET_STOPPED_BY_SW_BREAKPOINT; 246 return 1; 247 } 248 249 *reason = TARGET_STOPPED_BY_NO_REASON; 250 return 0; 251 } 252 253 /* Implement "show record debug" command. */ 254 255 static void 256 show_record_debug (struct ui_file *file, int from_tty, 257 struct cmd_list_element *c, const char *value) 258 { 259 fprintf_filtered (file, _("Debugging of process record target is %s.\n"), 260 value); 261 } 262 263 /* Alias for "target record". */ 264 265 static void 266 cmd_record_start (char *args, int from_tty) 267 { 268 execute_command ((char *) "target record-full", from_tty); 269 } 270 271 /* Truncate the record log from the present point 272 of replay until the end. */ 273 274 static void 275 cmd_record_delete (char *args, int from_tty) 276 { 277 require_record_target (); 278 279 if (!target_record_is_replaying (inferior_ptid)) 280 { 281 printf_unfiltered (_("Already at end of record list.\n")); 282 return; 283 } 284 285 if (!target_supports_delete_record ()) 286 { 287 printf_unfiltered (_("The current record target does not support " 288 "this operation.\n")); 289 return; 290 } 291 292 if (!from_tty || query (_("Delete the log from this point forward " 293 "and begin to record the running message " 294 "at current PC?"))) 295 target_delete_record (); 296 } 297 298 /* Implement the "stoprecord" or "record stop" command. */ 299 300 static void 301 cmd_record_stop (char *args, int from_tty) 302 { 303 struct target_ops *t; 304 305 t = require_record_target (); 306 307 record_stop (t); 308 record_unpush (t); 309 310 printf_unfiltered (_("Process record is stopped and all execution " 311 "logs are deleted.\n")); 312 313 observer_notify_record_changed (current_inferior (), 0, NULL, NULL); 314 } 315 316 /* The "set record" command. */ 317 318 static void 319 set_record_command (char *args, int from_tty) 320 { 321 printf_unfiltered (_("\"set record\" must be followed " 322 "by an apporpriate subcommand.\n")); 323 help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); 324 } 325 326 /* The "show record" command. */ 327 328 static void 329 show_record_command (char *args, int from_tty) 330 { 331 cmd_show_list (show_record_cmdlist, from_tty, ""); 332 } 333 334 /* The "info record" command. */ 335 336 static void 337 info_record_command (char *args, int from_tty) 338 { 339 struct target_ops *t; 340 341 t = find_record_target (); 342 if (t == NULL) 343 { 344 printf_filtered (_("No record target is currently active.\n")); 345 return; 346 } 347 348 printf_filtered (_("Active record target: %s\n"), t->to_shortname); 349 t->to_info_record (t); 350 } 351 352 /* The "record save" command. */ 353 354 static void 355 cmd_record_save (char *args, int from_tty) 356 { 357 char *recfilename, recfilename_buffer[40]; 358 359 require_record_target (); 360 361 if (args != NULL && *args != 0) 362 recfilename = args; 363 else 364 { 365 /* Default recfile name is "gdb_record.PID". */ 366 xsnprintf (recfilename_buffer, sizeof (recfilename_buffer), 367 "gdb_record.%d", ptid_get_pid (inferior_ptid)); 368 recfilename = recfilename_buffer; 369 } 370 371 target_save_record (recfilename); 372 } 373 374 /* See record.h. */ 375 376 void 377 record_goto (const char *arg) 378 { 379 ULONGEST insn; 380 381 if (arg == NULL || *arg == '\0') 382 error (_("Command requires an argument (insn number to go to).")); 383 384 insn = parse_and_eval_long (arg); 385 386 require_record_target (); 387 target_goto_record (insn); 388 } 389 390 /* "record goto" command. Argument is an instruction number, 391 as given by "info record". 392 393 Rewinds the recording (forward or backward) to the given instruction. */ 394 395 static void 396 cmd_record_goto (char *arg, int from_tty) 397 { 398 record_goto (arg); 399 } 400 401 /* The "record goto begin" command. */ 402 403 static void 404 cmd_record_goto_begin (char *arg, int from_tty) 405 { 406 if (arg != NULL && *arg != '\0') 407 error (_("Junk after argument: %s."), arg); 408 409 require_record_target (); 410 target_goto_record_begin (); 411 } 412 413 /* The "record goto end" command. */ 414 415 static void 416 cmd_record_goto_end (char *arg, int from_tty) 417 { 418 if (arg != NULL && *arg != '\0') 419 error (_("Junk after argument: %s."), arg); 420 421 require_record_target (); 422 target_goto_record_end (); 423 } 424 425 /* Read an instruction number from an argument string. */ 426 427 static ULONGEST 428 get_insn_number (char **arg) 429 { 430 ULONGEST number; 431 const char *begin, *end, *pos; 432 433 begin = *arg; 434 pos = skip_spaces_const (begin); 435 436 if (!isdigit (*pos)) 437 error (_("Expected positive number, got: %s."), pos); 438 439 number = strtoulst (pos, &end, 10); 440 441 *arg += (end - begin); 442 443 return number; 444 } 445 446 /* Read a context size from an argument string. */ 447 448 static int 449 get_context_size (char **arg) 450 { 451 char *pos; 452 int number; 453 454 pos = skip_spaces (*arg); 455 456 if (!isdigit (*pos)) 457 error (_("Expected positive number, got: %s."), pos); 458 459 return strtol (pos, arg, 10); 460 } 461 462 /* Complain about junk at the end of an argument string. */ 463 464 static void 465 no_chunk (char *arg) 466 { 467 if (*arg != 0) 468 error (_("Junk after argument: %s."), arg); 469 } 470 471 /* Read instruction-history modifiers from an argument string. */ 472 473 static int 474 get_insn_history_modifiers (char **arg) 475 { 476 int modifiers; 477 char *args; 478 479 modifiers = 0; 480 args = *arg; 481 482 if (args == NULL) 483 return modifiers; 484 485 while (*args == '/') 486 { 487 ++args; 488 489 if (*args == '\0') 490 error (_("Missing modifier.")); 491 492 for (; *args; ++args) 493 { 494 if (isspace (*args)) 495 break; 496 497 if (*args == '/') 498 continue; 499 500 switch (*args) 501 { 502 case 'm': 503 case 's': 504 modifiers |= DISASSEMBLY_SOURCE; 505 modifiers |= DISASSEMBLY_FILENAME; 506 break; 507 case 'r': 508 modifiers |= DISASSEMBLY_RAW_INSN; 509 break; 510 case 'f': 511 modifiers |= DISASSEMBLY_OMIT_FNAME; 512 break; 513 case 'p': 514 modifiers |= DISASSEMBLY_OMIT_PC; 515 break; 516 default: 517 error (_("Invalid modifier: %c."), *args); 518 } 519 } 520 521 args = skip_spaces (args); 522 } 523 524 /* Update the argument string. */ 525 *arg = args; 526 527 return modifiers; 528 } 529 530 /* The "set record instruction-history-size / set record 531 function-call-history-size" commands are unsigned, with UINT_MAX 532 meaning unlimited. The target interfaces works with signed int 533 though, to indicate direction, so map "unlimited" to INT_MAX, which 534 is about the same as unlimited in practice. If the user does have 535 a log that huge, she can fetch it in chunks across several requests, 536 but she'll likely have other problems first... */ 537 538 static int 539 command_size_to_target_size (unsigned int size) 540 { 541 gdb_assert (size <= INT_MAX || size == UINT_MAX); 542 543 if (size == UINT_MAX) 544 return INT_MAX; 545 else 546 return size; 547 } 548 549 /* The "record instruction-history" command. */ 550 551 static void 552 cmd_record_insn_history (char *arg, int from_tty) 553 { 554 int flags, size; 555 556 require_record_target (); 557 558 flags = get_insn_history_modifiers (&arg); 559 560 size = command_size_to_target_size (record_insn_history_size); 561 562 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 563 target_insn_history (size, flags); 564 else if (strcmp (arg, "-") == 0) 565 target_insn_history (-size, flags); 566 else 567 { 568 ULONGEST begin, end; 569 570 begin = get_insn_number (&arg); 571 572 if (*arg == ',') 573 { 574 arg = skip_spaces (++arg); 575 576 if (*arg == '+') 577 { 578 arg += 1; 579 size = get_context_size (&arg); 580 581 no_chunk (arg); 582 583 target_insn_history_from (begin, size, flags); 584 } 585 else if (*arg == '-') 586 { 587 arg += 1; 588 size = get_context_size (&arg); 589 590 no_chunk (arg); 591 592 target_insn_history_from (begin, -size, flags); 593 } 594 else 595 { 596 end = get_insn_number (&arg); 597 598 no_chunk (arg); 599 600 target_insn_history_range (begin, end, flags); 601 } 602 } 603 else 604 { 605 no_chunk (arg); 606 607 target_insn_history_from (begin, size, flags); 608 } 609 610 dont_repeat (); 611 } 612 } 613 614 /* Read function-call-history modifiers from an argument string. */ 615 616 static int 617 get_call_history_modifiers (char **arg) 618 { 619 int modifiers; 620 char *args; 621 622 modifiers = 0; 623 args = *arg; 624 625 if (args == NULL) 626 return modifiers; 627 628 while (*args == '/') 629 { 630 ++args; 631 632 if (*args == '\0') 633 error (_("Missing modifier.")); 634 635 for (; *args; ++args) 636 { 637 if (isspace (*args)) 638 break; 639 640 if (*args == '/') 641 continue; 642 643 switch (*args) 644 { 645 case 'l': 646 modifiers |= RECORD_PRINT_SRC_LINE; 647 break; 648 case 'i': 649 modifiers |= RECORD_PRINT_INSN_RANGE; 650 break; 651 case 'c': 652 modifiers |= RECORD_PRINT_INDENT_CALLS; 653 break; 654 default: 655 error (_("Invalid modifier: %c."), *args); 656 } 657 } 658 659 args = skip_spaces (args); 660 } 661 662 /* Update the argument string. */ 663 *arg = args; 664 665 return modifiers; 666 } 667 668 /* The "record function-call-history" command. */ 669 670 static void 671 cmd_record_call_history (char *arg, int from_tty) 672 { 673 int flags, size; 674 675 require_record_target (); 676 677 flags = get_call_history_modifiers (&arg); 678 679 size = command_size_to_target_size (record_call_history_size); 680 681 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 682 target_call_history (size, flags); 683 else if (strcmp (arg, "-") == 0) 684 target_call_history (-size, flags); 685 else 686 { 687 ULONGEST begin, end; 688 689 begin = get_insn_number (&arg); 690 691 if (*arg == ',') 692 { 693 arg = skip_spaces (++arg); 694 695 if (*arg == '+') 696 { 697 arg += 1; 698 size = get_context_size (&arg); 699 700 no_chunk (arg); 701 702 target_call_history_from (begin, size, flags); 703 } 704 else if (*arg == '-') 705 { 706 arg += 1; 707 size = get_context_size (&arg); 708 709 no_chunk (arg); 710 711 target_call_history_from (begin, -size, flags); 712 } 713 else 714 { 715 end = get_insn_number (&arg); 716 717 no_chunk (arg); 718 719 target_call_history_range (begin, end, flags); 720 } 721 } 722 else 723 { 724 no_chunk (arg); 725 726 target_call_history_from (begin, size, flags); 727 } 728 729 dont_repeat (); 730 } 731 } 732 733 /* Helper for "set record instruction-history-size" and "set record 734 function-call-history-size" input validation. COMMAND_VAR is the 735 variable registered in the command as control variable. *SETTING 736 is the real setting the command allows changing. */ 737 738 static void 739 validate_history_size (unsigned int *command_var, unsigned int *setting) 740 { 741 if (*command_var != UINT_MAX && *command_var > INT_MAX) 742 { 743 unsigned int new_value = *command_var; 744 745 /* Restore previous value. */ 746 *command_var = *setting; 747 error (_("integer %u out of range"), new_value); 748 } 749 750 /* Commit new value. */ 751 *setting = *command_var; 752 } 753 754 /* Called by do_setshow_command. We only want values in the 755 [0..INT_MAX] range, while the command's machinery accepts 756 [0..UINT_MAX]. See command_size_to_target_size. */ 757 758 static void 759 set_record_insn_history_size (char *args, int from_tty, 760 struct cmd_list_element *c) 761 { 762 validate_history_size (&record_insn_history_size_setshow_var, 763 &record_insn_history_size); 764 } 765 766 /* Called by do_setshow_command. We only want values in the 767 [0..INT_MAX] range, while the command's machinery accepts 768 [0..UINT_MAX]. See command_size_to_target_size. */ 769 770 static void 771 set_record_call_history_size (char *args, int from_tty, 772 struct cmd_list_element *c) 773 { 774 validate_history_size (&record_call_history_size_setshow_var, 775 &record_call_history_size); 776 } 777 778 /* Provide a prototype to silence -Wmissing-prototypes. */ 779 extern initialize_file_ftype _initialize_record; 780 781 void 782 _initialize_record (void) 783 { 784 struct cmd_list_element *c; 785 786 add_setshow_zuinteger_cmd ("record", no_class, &record_debug, 787 _("Set debugging of record/replay feature."), 788 _("Show debugging of record/replay feature."), 789 _("When enabled, debugging output for " 790 "record/replay feature is displayed."), 791 NULL, show_record_debug, &setdebuglist, 792 &showdebuglist); 793 794 add_setshow_uinteger_cmd ("instruction-history-size", no_class, 795 &record_insn_history_size_setshow_var, _("\ 796 Set number of instructions to print in \"record instruction-history\"."), _("\ 797 Show number of instructions to print in \"record instruction-history\"."), _("\ 798 A size of \"unlimited\" means unlimited instructions. The default is 10."), 799 set_record_insn_history_size, NULL, 800 &set_record_cmdlist, &show_record_cmdlist); 801 802 add_setshow_uinteger_cmd ("function-call-history-size", no_class, 803 &record_call_history_size_setshow_var, _("\ 804 Set number of function to print in \"record function-call-history\"."), _("\ 805 Show number of functions to print in \"record function-call-history\"."), _("\ 806 A size of \"unlimited\" means unlimited lines. The default is 10."), 807 set_record_call_history_size, NULL, 808 &set_record_cmdlist, &show_record_cmdlist); 809 810 c = add_prefix_cmd ("record", class_obscure, cmd_record_start, 811 _("Start recording."), 812 &record_cmdlist, "record ", 0, &cmdlist); 813 set_cmd_completer (c, filename_completer); 814 815 add_com_alias ("rec", "record", class_obscure, 1); 816 add_prefix_cmd ("record", class_support, set_record_command, 817 _("Set record options"), &set_record_cmdlist, 818 "set record ", 0, &setlist); 819 add_alias_cmd ("rec", "record", class_obscure, 1, &setlist); 820 add_prefix_cmd ("record", class_support, show_record_command, 821 _("Show record options"), &show_record_cmdlist, 822 "show record ", 0, &showlist); 823 add_alias_cmd ("rec", "record", class_obscure, 1, &showlist); 824 add_prefix_cmd ("record", class_support, info_record_command, 825 _("Info record options"), &info_record_cmdlist, 826 "info record ", 0, &infolist); 827 add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); 828 829 c = add_cmd ("save", class_obscure, cmd_record_save, 830 _("Save the execution log to a file.\n\ 831 Argument is optional filename.\n\ 832 Default filename is 'gdb_record.<process_id>'."), 833 &record_cmdlist); 834 set_cmd_completer (c, filename_completer); 835 836 add_cmd ("delete", class_obscure, cmd_record_delete, 837 _("Delete the rest of execution log and start recording it anew."), 838 &record_cmdlist); 839 add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist); 840 add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist); 841 842 add_cmd ("stop", class_obscure, cmd_record_stop, 843 _("Stop the record/replay target."), 844 &record_cmdlist); 845 add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); 846 847 add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\ 848 Restore the program to its state at instruction number N.\n\ 849 Argument is instruction number, as shown by 'info record'."), 850 &record_goto_cmdlist, "record goto ", 1, &record_cmdlist); 851 852 add_cmd ("begin", class_obscure, cmd_record_goto_begin, 853 _("Go to the beginning of the execution log."), 854 &record_goto_cmdlist); 855 add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist); 856 857 add_cmd ("end", class_obscure, cmd_record_goto_end, 858 _("Go to the end of the execution log."), 859 &record_goto_cmdlist); 860 861 add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\ 862 Print disassembled instructions stored in the execution log.\n\ 863 With a /m or /s modifier, source lines are included (if available).\n\ 864 With a /r modifier, raw instructions in hex are included.\n\ 865 With a /f modifier, function names are omitted.\n\ 866 With a /p modifier, current position markers are omitted.\n\ 867 With no argument, disassembles ten more instructions after the previous \ 868 disassembly.\n\ 869 \"record instruction-history -\" disassembles ten instructions before a \ 870 previous disassembly.\n\ 871 One argument specifies an instruction number as shown by 'info record', and \ 872 ten instructions are disassembled after that instruction.\n\ 873 Two arguments with comma between them specify starting and ending instruction \ 874 numbers to disassemble.\n\ 875 If the second argument is preceded by '+' or '-', it specifies the distance \ 876 from the first argument.\n\ 877 The number of instructions to disassemble can be defined with \"set record \ 878 instruction-history-size\"."), 879 &record_cmdlist); 880 881 add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\ 882 Prints the execution history at function granularity.\n\ 883 It prints one line for each sequence of instructions that belong to the same \ 884 function.\n\ 885 Without modifiers, it prints the function name.\n\ 886 With a /l modifier, the source file and line number range is included.\n\ 887 With a /i modifier, the instruction number range is included.\n\ 888 With a /c modifier, the output is indented based on the call stack depth.\n\ 889 With no argument, prints ten more lines after the previous ten-line print.\n\ 890 \"record function-call-history -\" prints ten lines before a previous ten-line \ 891 print.\n\ 892 One argument specifies a function number as shown by 'info record', and \ 893 ten lines are printed after that function.\n\ 894 Two arguments with comma between them specify a range of functions to print.\n\ 895 If the second argument is preceded by '+' or '-', it specifies the distance \ 896 from the first argument.\n\ 897 The number of functions to print can be defined with \"set record \ 898 function-call-history-size\"."), 899 &record_cmdlist); 900 901 /* Sync command control variables. */ 902 record_insn_history_size_setshow_var = record_insn_history_size; 903 record_call_history_size_setshow_var = record_call_history_size; 904 } 905