1 /* Process record and replay target for GDB, the GNU debugger. 2 3 Copyright (C) 2008-2013 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 number of functions to print in "record function-call-history". */ 39 static unsigned int record_call_history_size = 10; 40 41 struct cmd_list_element *record_cmdlist = NULL; 42 struct cmd_list_element *set_record_cmdlist = NULL; 43 struct cmd_list_element *show_record_cmdlist = NULL; 44 struct cmd_list_element *info_record_cmdlist = NULL; 45 46 #define DEBUG(msg, args...) \ 47 if (record_debug) \ 48 fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args) 49 50 /* Find the record target in the target stack. */ 51 52 static struct target_ops * 53 find_record_target (void) 54 { 55 struct target_ops *t; 56 57 for (t = current_target.beneath; t != NULL; t = t->beneath) 58 if (t->to_stratum == record_stratum) 59 return t; 60 61 return NULL; 62 } 63 64 /* Check that recording is active. Throw an error, if it isn't. */ 65 66 static struct target_ops * 67 require_record_target (void) 68 { 69 struct target_ops *t; 70 71 t = find_record_target (); 72 if (t == NULL) 73 error (_("No record target is currently active.\n" 74 "Use one of the \"target record-<tab><tab>\" commands first.")); 75 76 return t; 77 } 78 79 /* See record.h. */ 80 81 int 82 record_read_memory (struct gdbarch *gdbarch, 83 CORE_ADDR memaddr, gdb_byte *myaddr, 84 ssize_t len) 85 { 86 int ret = target_read_memory (memaddr, myaddr, len); 87 88 if (ret != 0) 89 DEBUG ("error reading memory at addr %s len = %ld.\n", 90 paddress (gdbarch, memaddr), (long) len); 91 92 return ret; 93 } 94 95 /* Stop recording. */ 96 97 static void 98 record_stop (struct target_ops *t) 99 { 100 DEBUG ("stop %s", t->to_shortname); 101 102 if (t->to_stop_recording != NULL) 103 t->to_stop_recording (); 104 } 105 106 /* Unpush the record target. */ 107 108 static void 109 record_unpush (struct target_ops *t) 110 { 111 DEBUG ("unpush %s", t->to_shortname); 112 113 unpush_target (t); 114 } 115 116 /* See record.h. */ 117 118 void 119 record_disconnect (struct target_ops *t, char *args, int from_tty) 120 { 121 gdb_assert (t->to_stratum == record_stratum); 122 123 DEBUG ("disconnect %s", t->to_shortname); 124 125 record_stop (t); 126 record_unpush (t); 127 128 target_disconnect (args, from_tty); 129 } 130 131 /* See record.h. */ 132 133 void 134 record_detach (struct target_ops *t, char *args, int from_tty) 135 { 136 gdb_assert (t->to_stratum == record_stratum); 137 138 DEBUG ("detach %s", t->to_shortname); 139 140 record_stop (t); 141 record_unpush (t); 142 143 target_detach (args, from_tty); 144 } 145 146 /* See record.h. */ 147 148 void 149 record_mourn_inferior (struct target_ops *t) 150 { 151 gdb_assert (t->to_stratum == record_stratum); 152 153 DEBUG ("mourn inferior %s", t->to_shortname); 154 155 /* It is safer to not stop recording. Resources will be freed when 156 threads are discarded. */ 157 record_unpush (t); 158 159 target_mourn_inferior (); 160 } 161 162 /* See record.h. */ 163 164 void 165 record_kill (struct target_ops *t) 166 { 167 gdb_assert (t->to_stratum == record_stratum); 168 169 DEBUG ("kill %s", t->to_shortname); 170 171 /* It is safer to not stop recording. Resources will be freed when 172 threads are discarded. */ 173 record_unpush (t); 174 175 target_kill (); 176 } 177 178 /* Implement "show record debug" command. */ 179 180 static void 181 show_record_debug (struct ui_file *file, int from_tty, 182 struct cmd_list_element *c, const char *value) 183 { 184 fprintf_filtered (file, _("Debugging of process record target is %s.\n"), 185 value); 186 } 187 188 /* Alias for "target record". */ 189 190 static void 191 cmd_record_start (char *args, int from_tty) 192 { 193 execute_command ("target record-full", from_tty); 194 } 195 196 /* Truncate the record log from the present point 197 of replay until the end. */ 198 199 static void 200 cmd_record_delete (char *args, int from_tty) 201 { 202 require_record_target (); 203 204 if (!target_record_is_replaying ()) 205 { 206 printf_unfiltered (_("Already at end of record list.\n")); 207 return; 208 } 209 210 if (!target_supports_delete_record ()) 211 { 212 printf_unfiltered (_("The current record target does not support " 213 "this operation.\n")); 214 return; 215 } 216 217 if (!from_tty || query (_("Delete the log from this point forward " 218 "and begin to record the running message " 219 "at current PC?"))) 220 target_delete_record (); 221 } 222 223 /* Implement the "stoprecord" or "record stop" command. */ 224 225 static void 226 cmd_record_stop (char *args, int from_tty) 227 { 228 struct target_ops *t; 229 230 t = require_record_target (); 231 232 record_stop (t); 233 record_unpush (t); 234 235 printf_unfiltered (_("Process record is stopped and all execution " 236 "logs are deleted.\n")); 237 238 observer_notify_record_changed (current_inferior (), 0); 239 } 240 241 /* The "set record" command. */ 242 243 static void 244 set_record_command (char *args, int from_tty) 245 { 246 printf_unfiltered (_("\"set record\" must be followed " 247 "by an apporpriate subcommand.\n")); 248 help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout); 249 } 250 251 /* The "show record" command. */ 252 253 static void 254 show_record_command (char *args, int from_tty) 255 { 256 cmd_show_list (show_record_cmdlist, from_tty, ""); 257 } 258 259 /* The "info record" command. */ 260 261 static void 262 info_record_command (char *args, int from_tty) 263 { 264 struct target_ops *t; 265 266 t = find_record_target (); 267 if (t == NULL) 268 { 269 printf_filtered (_("No record target is currently active.\n")); 270 return; 271 } 272 273 printf_filtered (_("Active record target: %s\n"), t->to_shortname); 274 if (t->to_info_record != NULL) 275 t->to_info_record (); 276 } 277 278 /* The "record save" command. */ 279 280 static void 281 cmd_record_save (char *args, int from_tty) 282 { 283 char *recfilename, recfilename_buffer[40]; 284 285 require_record_target (); 286 287 if (args != NULL && *args != 0) 288 recfilename = args; 289 else 290 { 291 /* Default recfile name is "gdb_record.PID". */ 292 xsnprintf (recfilename_buffer, sizeof (recfilename_buffer), 293 "gdb_record.%d", PIDGET (inferior_ptid)); 294 recfilename = recfilename_buffer; 295 } 296 297 target_save_record (recfilename); 298 } 299 300 /* "record goto" command. Argument is an instruction number, 301 as given by "info record". 302 303 Rewinds the recording (forward or backward) to the given instruction. */ 304 305 void 306 cmd_record_goto (char *arg, int from_tty) 307 { 308 require_record_target (); 309 310 if (arg == NULL || *arg == '\0') 311 error (_("Command requires an argument (insn number to go to).")); 312 313 if (strncmp (arg, "start", strlen ("start")) == 0 314 || strncmp (arg, "begin", strlen ("begin")) == 0) 315 target_goto_record_begin (); 316 else if (strncmp (arg, "end", strlen ("end")) == 0) 317 target_goto_record_end (); 318 else 319 { 320 ULONGEST insn; 321 322 insn = parse_and_eval_long (arg); 323 target_goto_record (insn); 324 } 325 } 326 327 /* Read an instruction number from an argument string. */ 328 329 static ULONGEST 330 get_insn_number (char **arg) 331 { 332 ULONGEST number; 333 const char *begin, *end, *pos; 334 335 begin = *arg; 336 pos = skip_spaces_const (begin); 337 338 if (!isdigit (*pos)) 339 error (_("Expected positive number, got: %s."), pos); 340 341 number = strtoulst (pos, &end, 10); 342 343 *arg += (end - begin); 344 345 return number; 346 } 347 348 /* Read a context size from an argument string. */ 349 350 static int 351 get_context_size (char **arg) 352 { 353 char *pos; 354 int number; 355 356 pos = skip_spaces (*arg); 357 358 if (!isdigit (*pos)) 359 error (_("Expected positive number, got: %s."), pos); 360 361 return strtol (pos, arg, 10); 362 } 363 364 /* Complain about junk at the end of an argument string. */ 365 366 static void 367 no_chunk (char *arg) 368 { 369 if (*arg != 0) 370 error (_("Junk after argument: %s."), arg); 371 } 372 373 /* Read instruction-history modifiers from an argument string. */ 374 375 static int 376 get_insn_history_modifiers (char **arg) 377 { 378 int modifiers; 379 char *args; 380 381 modifiers = 0; 382 args = *arg; 383 384 if (args == NULL) 385 return modifiers; 386 387 while (*args == '/') 388 { 389 ++args; 390 391 if (*args == '\0') 392 error (_("Missing modifier.")); 393 394 for (; *args; ++args) 395 { 396 if (isspace (*args)) 397 break; 398 399 if (*args == '/') 400 continue; 401 402 switch (*args) 403 { 404 case 'm': 405 modifiers |= DISASSEMBLY_SOURCE; 406 modifiers |= DISASSEMBLY_FILENAME; 407 break; 408 case 'r': 409 modifiers |= DISASSEMBLY_RAW_INSN; 410 break; 411 case 'f': 412 modifiers |= DISASSEMBLY_OMIT_FNAME; 413 break; 414 case 'p': 415 modifiers |= DISASSEMBLY_OMIT_PC; 416 break; 417 default: 418 error (_("Invalid modifier: %c."), *args); 419 } 420 } 421 422 args = skip_spaces (args); 423 } 424 425 /* Update the argument string. */ 426 *arg = args; 427 428 return modifiers; 429 } 430 431 /* The "record instruction-history" command. */ 432 433 static void 434 cmd_record_insn_history (char *arg, int from_tty) 435 { 436 int flags, size; 437 438 require_record_target (); 439 440 flags = get_insn_history_modifiers (&arg); 441 442 /* We use a signed size to also indicate the direction. Make sure that 443 unlimited remains unlimited. */ 444 size = (int) record_insn_history_size; 445 if (size < 0) 446 size = INT_MAX; 447 448 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 449 target_insn_history (size, flags); 450 else if (strcmp (arg, "-") == 0) 451 target_insn_history (-size, flags); 452 else 453 { 454 ULONGEST begin, end; 455 456 begin = get_insn_number (&arg); 457 458 if (*arg == ',') 459 { 460 arg = skip_spaces (++arg); 461 462 if (*arg == '+') 463 { 464 arg += 1; 465 size = get_context_size (&arg); 466 467 no_chunk (arg); 468 469 target_insn_history_from (begin, size, flags); 470 } 471 else if (*arg == '-') 472 { 473 arg += 1; 474 size = get_context_size (&arg); 475 476 no_chunk (arg); 477 478 target_insn_history_from (begin, -size, flags); 479 } 480 else 481 { 482 end = get_insn_number (&arg); 483 484 no_chunk (arg); 485 486 target_insn_history_range (begin, end, flags); 487 } 488 } 489 else 490 { 491 no_chunk (arg); 492 493 target_insn_history_from (begin, size, flags); 494 } 495 496 dont_repeat (); 497 } 498 } 499 500 /* Read function-call-history modifiers from an argument string. */ 501 502 static int 503 get_call_history_modifiers (char **arg) 504 { 505 int modifiers; 506 char *args; 507 508 modifiers = 0; 509 args = *arg; 510 511 if (args == NULL) 512 return modifiers; 513 514 while (*args == '/') 515 { 516 ++args; 517 518 if (*args == '\0') 519 error (_("Missing modifier.")); 520 521 for (; *args; ++args) 522 { 523 if (isspace (*args)) 524 break; 525 526 if (*args == '/') 527 continue; 528 529 switch (*args) 530 { 531 case 'l': 532 modifiers |= record_print_src_line; 533 break; 534 case 'i': 535 modifiers |= record_print_insn_range; 536 break; 537 default: 538 error (_("Invalid modifier: %c."), *args); 539 } 540 } 541 542 args = skip_spaces (args); 543 } 544 545 /* Update the argument string. */ 546 *arg = args; 547 548 return modifiers; 549 } 550 551 /* The "record function-call-history" command. */ 552 553 static void 554 cmd_record_call_history (char *arg, int from_tty) 555 { 556 int flags, size; 557 558 require_record_target (); 559 560 flags = get_call_history_modifiers (&arg); 561 562 /* We use a signed size to also indicate the direction. Make sure that 563 unlimited remains unlimited. */ 564 size = (int) record_call_history_size; 565 if (size < 0) 566 size = INT_MAX; 567 568 if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) 569 target_call_history (size, flags); 570 else if (strcmp (arg, "-") == 0) 571 target_call_history (-size, flags); 572 else 573 { 574 ULONGEST begin, end; 575 576 begin = get_insn_number (&arg); 577 578 if (*arg == ',') 579 { 580 arg = skip_spaces (++arg); 581 582 if (*arg == '+') 583 { 584 arg += 1; 585 size = get_context_size (&arg); 586 587 no_chunk (arg); 588 589 target_call_history_from (begin, size, flags); 590 } 591 else if (*arg == '-') 592 { 593 arg += 1; 594 size = get_context_size (&arg); 595 596 no_chunk (arg); 597 598 target_call_history_from (begin, -size, flags); 599 } 600 else 601 { 602 end = get_insn_number (&arg); 603 604 no_chunk (arg); 605 606 target_call_history_range (begin, end, flags); 607 } 608 } 609 else 610 { 611 no_chunk (arg); 612 613 target_call_history_from (begin, size, flags); 614 } 615 616 dont_repeat (); 617 } 618 } 619 620 /* Provide a prototype to silence -Wmissing-prototypes. */ 621 extern initialize_file_ftype _initialize_record; 622 623 void 624 _initialize_record (void) 625 { 626 struct cmd_list_element *c; 627 628 add_setshow_zuinteger_cmd ("record", no_class, &record_debug, 629 _("Set debugging of record/replay feature."), 630 _("Show debugging of record/replay feature."), 631 _("When enabled, debugging output for " 632 "record/replay feature is displayed."), 633 NULL, show_record_debug, &setdebuglist, 634 &showdebuglist); 635 636 add_setshow_uinteger_cmd ("instruction-history-size", no_class, 637 &record_insn_history_size, _("\ 638 Set number of instructions to print in \"record instruction-history\"."), _("\ 639 Show number of instructions to print in \"record instruction-history\"."), 640 NULL, NULL, NULL, &set_record_cmdlist, 641 &show_record_cmdlist); 642 643 add_setshow_uinteger_cmd ("function-call-history-size", no_class, 644 &record_call_history_size, _("\ 645 Set number of function to print in \"record function-call-history\"."), _("\ 646 Show number of functions to print in \"record function-call-history\"."), 647 NULL, NULL, NULL, &set_record_cmdlist, 648 &show_record_cmdlist); 649 650 c = add_prefix_cmd ("record", class_obscure, cmd_record_start, 651 _("Start recording."), 652 &record_cmdlist, "record ", 0, &cmdlist); 653 set_cmd_completer (c, filename_completer); 654 655 add_com_alias ("rec", "record", class_obscure, 1); 656 add_prefix_cmd ("record", class_support, set_record_command, 657 _("Set record options"), &set_record_cmdlist, 658 "set record ", 0, &setlist); 659 add_alias_cmd ("rec", "record", class_obscure, 1, &setlist); 660 add_prefix_cmd ("record", class_support, show_record_command, 661 _("Show record options"), &show_record_cmdlist, 662 "show record ", 0, &showlist); 663 add_alias_cmd ("rec", "record", class_obscure, 1, &showlist); 664 add_prefix_cmd ("record", class_support, info_record_command, 665 _("Info record options"), &info_record_cmdlist, 666 "info record ", 0, &infolist); 667 add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); 668 669 c = add_cmd ("save", class_obscure, cmd_record_save, 670 _("Save the execution log to a file.\n\ 671 Argument is optional filename.\n\ 672 Default filename is 'gdb_record.<process_id>'."), 673 &record_cmdlist); 674 set_cmd_completer (c, filename_completer); 675 676 add_cmd ("delete", class_obscure, cmd_record_delete, 677 _("Delete the rest of execution log and start recording it anew."), 678 &record_cmdlist); 679 add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist); 680 add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist); 681 682 add_cmd ("stop", class_obscure, cmd_record_stop, 683 _("Stop the record/replay target."), 684 &record_cmdlist); 685 add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); 686 687 add_cmd ("goto", class_obscure, cmd_record_goto, _("\ 688 Restore the program to its state at instruction number N.\n\ 689 Argument is instruction number, as shown by 'info record'."), 690 &record_cmdlist); 691 692 add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\ 693 Print disassembled instructions stored in the execution log.\n\ 694 With a /m modifier, source lines are included (if available).\n\ 695 With a /r modifier, raw instructions in hex are included.\n\ 696 With a /f modifier, function names are omitted.\n\ 697 With a /p modifier, current position markers are omitted.\n\ 698 With no argument, disassembles ten more instructions after the previous \ 699 disassembly.\n\ 700 \"record instruction-history -\" disassembles ten instructions before a \ 701 previous disassembly.\n\ 702 One argument specifies an instruction number as shown by 'info record', and \ 703 ten instructions are disassembled after that instruction.\n\ 704 Two arguments with comma between them specify starting and ending instruction \ 705 numbers to disassemble.\n\ 706 If the second argument is preceded by '+' or '-', it specifies the distance \ 707 from the first argument.\n\ 708 The number of instructions to disassemble can be defined with \"set record \ 709 instruction-history-size\"."), 710 &record_cmdlist); 711 712 add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\ 713 Prints the execution history at function granularity.\n\ 714 It prints one line for each sequence of instructions that belong to the same \ 715 function.\n\ 716 Without modifiers, it prints the function name.\n\ 717 With a /l modifier, the source file and line number range is included.\n\ 718 With a /i modifier, the instruction number range is included.\n\ 719 With no argument, prints ten more lines after the previous ten-line print.\n\ 720 \"record function-call-history -\" prints ten lines before a previous ten-line \ 721 print.\n\ 722 One argument specifies a function number as shown by 'info record', and \ 723 ten lines are printed after that function.\n\ 724 Two arguments with comma between them specify a range of functions to print.\n\ 725 If the second argument is preceded by '+' or '-', it specifies the distance \ 726 from the first argument.\n\ 727 The number of functions to print can be defined with \"set record \ 728 function-call-history-size\"."), 729 &record_cmdlist); 730 } 731