X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gdb%2Frecord.c;h=ff54a8bd0da659e1360c8b8d9b68051b4cd195fb;hb=5aa03310cef09e8c41aaa05152d9570dfe62ba81;hp=ea8e7df3a78dded73e5e09988d697fed3d669679;hpb=67c86d068313124e6c73e6ec0294866fc0471268;p=platform%2Fupstream%2Fbinutils.git diff --git a/gdb/record.c b/gdb/record.c index ea8e7df..ff54a8b 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -1,6 +1,6 @@ /* Process record and replay target for GDB, the GNU debugger. - Copyright (C) 2008-2013 Free Software Foundation, Inc. + Copyright (C) 2008-2014 Free Software Foundation, Inc. This file is part of GDB. @@ -35,7 +35,20 @@ unsigned int record_debug = 0; /* The number of instructions to print in "record instruction-history". */ static unsigned int record_insn_history_size = 10; +/* The variable registered as control variable in the "record + instruction-history" command. Necessary for extra input + validation. */ +static unsigned int record_insn_history_size_setshow_var; + +/* The number of functions to print in "record function-call-history". */ +static unsigned int record_call_history_size = 10; + +/* The variable registered as control variable in the "record + call-history" command. Necessary for extra input validation. */ +static unsigned int record_call_history_size_setshow_var; + struct cmd_list_element *record_cmdlist = NULL; +struct cmd_list_element *record_goto_cmdlist = NULL; struct cmd_list_element *set_record_cmdlist = NULL; struct cmd_list_element *show_record_cmdlist = NULL; struct cmd_list_element *info_record_cmdlist = NULL; @@ -44,18 +57,12 @@ struct cmd_list_element *info_record_cmdlist = NULL; if (record_debug) \ fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args) -/* Find the record target in the target stack. */ +/* See record.h. */ -static struct target_ops * +struct target_ops * find_record_target (void) { - struct target_ops *t; - - for (t = current_target.beneath; t != NULL; t = t->beneath) - if (t->to_stratum == record_stratum) - return t; - - return NULL; + return find_target_at (record_stratum); } /* Check that recording is active. Throw an error, if it isn't. */ @@ -75,6 +82,17 @@ require_record_target (void) /* See record.h. */ +void +record_preopen (void) +{ + /* Check if a record target is already running. */ + if (find_record_target () != NULL) + error (_("The process is already being recorded. Use \"record stop\" to " + "stop recording first.")); +} + +/* See record.h. */ + int record_read_memory (struct gdbarch *gdbarch, CORE_ADDR memaddr, gdb_byte *myaddr, @@ -96,8 +114,7 @@ record_stop (struct target_ops *t) { DEBUG ("stop %s", t->to_shortname); - if (t->to_stop_recording != NULL) - t->to_stop_recording (); + t->to_stop_recording (t); } /* Unpush the record target. */ @@ -113,7 +130,7 @@ record_unpush (struct target_ops *t) /* See record.h. */ void -record_disconnect (struct target_ops *t, char *args, int from_tty) +record_disconnect (struct target_ops *t, const char *args, int from_tty) { gdb_assert (t->to_stratum == record_stratum); @@ -128,7 +145,7 @@ record_disconnect (struct target_ops *t, char *args, int from_tty) /* See record.h. */ void -record_detach (struct target_ops *t, char *args, int from_tty) +record_detach (struct target_ops *t, const char *args, int from_tty) { gdb_assert (t->to_stratum == record_stratum); @@ -268,8 +285,7 @@ info_record_command (char *args, int from_tty) } printf_filtered (_("Active record target: %s\n"), t->to_shortname); - if (t->to_info_record != NULL) - t->to_info_record (); + t->to_info_record (t); } /* The "record save" command. */ @@ -287,38 +303,62 @@ cmd_record_save (char *args, int from_tty) { /* Default recfile name is "gdb_record.PID". */ xsnprintf (recfilename_buffer, sizeof (recfilename_buffer), - "gdb_record.%d", PIDGET (inferior_ptid)); + "gdb_record.%d", ptid_get_pid (inferior_ptid)); recfilename = recfilename_buffer; } target_save_record (recfilename); } +/* See record.h. */ + +void +record_goto (const char *arg) +{ + ULONGEST insn; + + if (arg == NULL || *arg == '\0') + error (_("Command requires an argument (insn number to go to).")); + + insn = parse_and_eval_long (arg); + + require_record_target (); + target_goto_record (insn); +} + /* "record goto" command. Argument is an instruction number, as given by "info record". Rewinds the recording (forward or backward) to the given instruction. */ -void +static void cmd_record_goto (char *arg, int from_tty) { + record_goto (arg); +} + +/* The "record goto begin" command. */ + +static void +cmd_record_goto_begin (char *arg, int from_tty) +{ + if (arg != NULL && *arg != '\0') + error (_("Junk after argument: %s."), arg); + require_record_target (); + target_goto_record_begin (); +} - if (arg == NULL || *arg == '\0') - error (_("Command requires an argument (insn number to go to).")); +/* The "record goto end" command. */ - if (strncmp (arg, "start", strlen ("start")) == 0 - || strncmp (arg, "begin", strlen ("begin")) == 0) - target_goto_record_begin (); - else if (strncmp (arg, "end", strlen ("end")) == 0) - target_goto_record_end (); - else - { - ULONGEST insn; +static void +cmd_record_goto_end (char *arg, int from_tty) +{ + if (arg != NULL && *arg != '\0') + error (_("Junk after argument: %s."), arg); - insn = parse_and_eval_long (arg); - target_goto_record (insn); - } + require_record_target (); + target_goto_record_end (); } /* Read an instruction number from an argument string. */ @@ -408,6 +448,9 @@ get_insn_history_modifiers (char **arg) case 'f': modifiers |= DISASSEMBLY_OMIT_FNAME; break; + case 'p': + modifiers |= DISASSEMBLY_OMIT_PC; + break; default: error (_("Invalid modifier: %c."), *args); } @@ -422,6 +465,25 @@ get_insn_history_modifiers (char **arg) return modifiers; } +/* The "set record instruction-history-size / set record + function-call-history-size" commands are unsigned, with UINT_MAX + meaning unlimited. The target interfaces works with signed int + though, to indicate direction, so map "unlimited" to INT_MAX, which + is about the same as unlimited in practice. If the user does have + a log that huge, she can fetch it in chunks across several requests, + but she'll likely have other problems first... */ + +static int +command_size_to_target_size (unsigned int size) +{ + gdb_assert (size <= INT_MAX || size == UINT_MAX); + + if (size == UINT_MAX) + return INT_MAX; + else + return size; +} + /* The "record instruction-history" command. */ static void @@ -433,11 +495,7 @@ cmd_record_insn_history (char *arg, int from_tty) flags = get_insn_history_modifiers (&arg); - /* We use a signed size to also indicate the direction. Make sure that - unlimited remains unlimited. */ - size = (int) record_insn_history_size; - if (size < 0) - size = INT_MAX; + size = command_size_to_target_size (record_insn_history_size); if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) target_insn_history (size, flags); @@ -491,6 +549,170 @@ cmd_record_insn_history (char *arg, int from_tty) } } +/* Read function-call-history modifiers from an argument string. */ + +static int +get_call_history_modifiers (char **arg) +{ + int modifiers; + char *args; + + modifiers = 0; + args = *arg; + + if (args == NULL) + return modifiers; + + while (*args == '/') + { + ++args; + + if (*args == '\0') + error (_("Missing modifier.")); + + for (; *args; ++args) + { + if (isspace (*args)) + break; + + if (*args == '/') + continue; + + switch (*args) + { + case 'l': + modifiers |= RECORD_PRINT_SRC_LINE; + break; + case 'i': + modifiers |= RECORD_PRINT_INSN_RANGE; + break; + case 'c': + modifiers |= RECORD_PRINT_INDENT_CALLS; + break; + default: + error (_("Invalid modifier: %c."), *args); + } + } + + args = skip_spaces (args); + } + + /* Update the argument string. */ + *arg = args; + + return modifiers; +} + +/* The "record function-call-history" command. */ + +static void +cmd_record_call_history (char *arg, int from_tty) +{ + int flags, size; + + require_record_target (); + + flags = get_call_history_modifiers (&arg); + + size = command_size_to_target_size (record_call_history_size); + + if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0) + target_call_history (size, flags); + else if (strcmp (arg, "-") == 0) + target_call_history (-size, flags); + else + { + ULONGEST begin, end; + + begin = get_insn_number (&arg); + + if (*arg == ',') + { + arg = skip_spaces (++arg); + + if (*arg == '+') + { + arg += 1; + size = get_context_size (&arg); + + no_chunk (arg); + + target_call_history_from (begin, size, flags); + } + else if (*arg == '-') + { + arg += 1; + size = get_context_size (&arg); + + no_chunk (arg); + + target_call_history_from (begin, -size, flags); + } + else + { + end = get_insn_number (&arg); + + no_chunk (arg); + + target_call_history_range (begin, end, flags); + } + } + else + { + no_chunk (arg); + + target_call_history_from (begin, size, flags); + } + + dont_repeat (); + } +} + +/* Helper for "set record instruction-history-size" and "set record + function-call-history-size" input validation. COMMAND_VAR is the + variable registered in the command as control variable. *SETTING + is the real setting the command allows changing. */ + +static void +validate_history_size (unsigned int *command_var, unsigned int *setting) +{ + if (*command_var != UINT_MAX && *command_var > INT_MAX) + { + unsigned int new_value = *command_var; + + /* Restore previous value. */ + *command_var = *setting; + error (_("integer %u out of range"), new_value); + } + + /* Commit new value. */ + *setting = *command_var; +} + +/* Called by do_setshow_command. We only want values in the + [0..INT_MAX] range, while the command's machinery accepts + [0..UINT_MAX]. See command_size_to_target_size. */ + +static void +set_record_insn_history_size (char *args, int from_tty, + struct cmd_list_element *c) +{ + validate_history_size (&record_insn_history_size_setshow_var, + &record_insn_history_size); +} + +/* Called by do_setshow_command. We only want values in the + [0..INT_MAX] range, while the command's machinery accepts + [0..UINT_MAX]. See command_size_to_target_size. */ + +static void +set_record_call_history_size (char *args, int from_tty, + struct cmd_list_element *c) +{ + validate_history_size (&record_call_history_size_setshow_var, + &record_call_history_size); +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_record; @@ -508,11 +730,20 @@ _initialize_record (void) &showdebuglist); add_setshow_uinteger_cmd ("instruction-history-size", no_class, - &record_insn_history_size, _("\ + &record_insn_history_size_setshow_var, _("\ Set number of instructions to print in \"record instruction-history\"."), _("\ -Show number of instructions to print in \"record instruction-history\"."), - NULL, NULL, NULL, &set_record_cmdlist, - &show_record_cmdlist); +Show number of instructions to print in \"record instruction-history\"."), _("\ +A size of \"unlimited\" means unlimited instructions. The default is 10."), + set_record_insn_history_size, NULL, + &set_record_cmdlist, &show_record_cmdlist); + + add_setshow_uinteger_cmd ("function-call-history-size", no_class, + &record_call_history_size_setshow_var, _("\ +Set number of function to print in \"record function-call-history\"."), _("\ +Show number of functions to print in \"record function-call-history\"."), _("\ +A size of \"unlimited\" means unlimited lines. The default is 10."), + set_record_call_history_size, NULL, + &set_record_cmdlist, &show_record_cmdlist); c = add_prefix_cmd ("record", class_obscure, cmd_record_start, _("Start recording."), @@ -551,16 +782,26 @@ Default filename is 'gdb_record.'."), &record_cmdlist); add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); - add_cmd ("goto", class_obscure, cmd_record_goto, _("\ + add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\ Restore the program to its state at instruction number N.\n\ Argument is instruction number, as shown by 'info record'."), - &record_cmdlist); + &record_goto_cmdlist, "record goto ", 1, &record_cmdlist); + + add_cmd ("begin", class_obscure, cmd_record_goto_begin, + _("Go to the beginning of the execution log."), + &record_goto_cmdlist); + add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist); + + add_cmd ("end", class_obscure, cmd_record_goto_end, + _("Go to the end of the execution log."), + &record_goto_cmdlist); add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\ Print disassembled instructions stored in the execution log.\n\ With a /m modifier, source lines are included (if available).\n\ With a /r modifier, raw instructions in hex are included.\n\ With a /f modifier, function names are omitted.\n\ +With a /p modifier, current position markers are omitted.\n\ With no argument, disassembles ten more instructions after the previous \ disassembly.\n\ \"record instruction-history -\" disassembles ten instructions before a \ @@ -574,4 +815,28 @@ from the first argument.\n\ The number of instructions to disassemble can be defined with \"set record \ instruction-history-size\"."), &record_cmdlist); + + add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\ +Prints the execution history at function granularity.\n\ +It prints one line for each sequence of instructions that belong to the same \ +function.\n\ +Without modifiers, it prints the function name.\n\ +With a /l modifier, the source file and line number range is included.\n\ +With a /i modifier, the instruction number range is included.\n\ +With a /c modifier, the output is indented based on the call stack depth.\n\ +With no argument, prints ten more lines after the previous ten-line print.\n\ +\"record function-call-history -\" prints ten lines before a previous ten-line \ +print.\n\ +One argument specifies a function number as shown by 'info record', and \ +ten lines are printed after that function.\n\ +Two arguments with comma between them specify a range of functions to print.\n\ +If the second argument is preceded by '+' or '-', it specifies the distance \ +from the first argument.\n\ +The number of functions to print can be defined with \"set record \ +function-call-history-size\"."), + &record_cmdlist); + + /* Sync command control variables. */ + record_insn_history_size_setshow_var = record_insn_history_size; + record_call_history_size_setshow_var = record_call_history_size; }