Collision between NT_GNU_BUILD_ATTRIBUTE_OPEN and NT_PPC_VMX
[deliverable/binutils-gdb.git] / gdb / record.c
index ea8e7df3a78dded73e5e09988d697fed3d669679..16e5b03fe84658be9a37da4a243dbfaa616908e1 100644 (file)
@@ -1,6 +1,6 @@
 /* Process record and replay target for GDB, the GNU debugger.
 
 /* Process record and replay target for GDB, the GNU debugger.
 
-   Copyright (C) 2008-2013 Free Software Foundation, Inc.
+   Copyright (C) 2008-2017 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
    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 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_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;
 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)
 
   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)
 {
 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.  */
 }
 
 /* Check that recording is active.  Throw an error, if it isn't.  */
@@ -75,6 +82,59 @@ require_record_target (void)
 
 /* See record.h.  */
 
 
 /* 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.  */
+
+void
+record_start (const char *method, const char *format, int from_tty)
+{
+  if (method == NULL)
+    {
+      if (format == NULL)
+       execute_command_to_string ((char *) "record", from_tty);
+      else
+       error (_("Invalid format."));
+    }
+  else if (strcmp (method, "full") == 0)
+    {
+      if (format == NULL)
+       execute_command_to_string ((char *) "record full", from_tty);
+      else
+       error (_("Invalid format."));
+    }
+  else if (strcmp (method, "btrace") == 0)
+    {
+      if (format == NULL)
+       execute_command_to_string ((char *) "record btrace", from_tty);
+      else if (strcmp (format, "bts") == 0)
+       execute_command_to_string ((char *) "record btrace bts", from_tty);
+      else if (strcmp (format, "pt") == 0)
+       execute_command_to_string ((char *) "record btrace pt", from_tty);
+      else
+       error (_("Invalid format."));
+    }
+  else
+    error (_("Invalid method."));
+}
+
+/* See record.h.  */
+
+void
+record_stop (int from_tty)
+{
+  execute_command_to_string ((char *) "record stop", from_tty);
+}
+
+/* See record.h.  */
+
 int
 record_read_memory (struct gdbarch *gdbarch,
                    CORE_ADDR memaddr, gdb_byte *myaddr,
 int
 record_read_memory (struct gdbarch *gdbarch,
                    CORE_ADDR memaddr, gdb_byte *myaddr,
@@ -96,8 +156,7 @@ record_stop (struct target_ops *t)
 {
   DEBUG ("stop %s", t->to_shortname);
 
 {
   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.  */
 }
 
 /* Unpush the record target.  */
@@ -113,7 +172,7 @@ record_unpush (struct target_ops *t)
 /* See record.h.  */
 
 void
 /* 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);
 
 {
   gdb_assert (t->to_stratum == record_stratum);
 
@@ -128,7 +187,7 @@ record_disconnect (struct target_ops *t, char *args, int from_tty)
 /* See record.h.  */
 
 void
 /* 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);
 
 {
   gdb_assert (t->to_stratum == record_stratum);
 
@@ -153,7 +212,7 @@ record_mourn_inferior (struct target_ops *t)
      threads are discarded.  */
   record_unpush (t);
 
      threads are discarded.  */
   record_unpush (t);
 
-  target_mourn_inferior ();
+  target_mourn_inferior (inferior_ptid);
 }
 
 /* See record.h.  */
 }
 
 /* See record.h.  */
@@ -172,6 +231,25 @@ record_kill (struct target_ops *t)
   target_kill ();
 }
 
   target_kill ();
 }
 
+/* See record.h.  */
+
+int
+record_check_stopped_by_breakpoint (struct address_space *aspace, CORE_ADDR pc,
+                                   enum target_stop_reason *reason)
+{
+  if (breakpoint_inserted_here_p (aspace, pc))
+    {
+      if (hardware_breakpoint_inserted_here_p (aspace, pc))
+       *reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+      else
+       *reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+      return 1;
+    }
+
+  *reason = TARGET_STOPPED_BY_NO_REASON;
+  return 0;
+}
+
 /* Implement "show record debug" command.  */
 
 static void
 /* Implement "show record debug" command.  */
 
 static void
@@ -187,7 +265,7 @@ show_record_debug (struct ui_file *file, int from_tty,
 static void
 cmd_record_start (char *args, int from_tty)
 {
 static void
 cmd_record_start (char *args, int from_tty)
 {
-  execute_command ("target record-full", from_tty);
+  execute_command ((char *) "target record-full", from_tty);
 }
 
 /* Truncate the record log from the present point
 }
 
 /* Truncate the record log from the present point
@@ -198,7 +276,7 @@ cmd_record_delete (char *args, int from_tty)
 {
   require_record_target ();
 
 {
   require_record_target ();
 
-  if (!target_record_is_replaying ())
+  if (!target_record_is_replaying (inferior_ptid))
     {
       printf_unfiltered (_("Already at end of record list.\n"));
       return;
     {
       printf_unfiltered (_("Already at end of record list.\n"));
       return;
@@ -232,7 +310,7 @@ cmd_record_stop (char *args, int from_tty)
   printf_unfiltered (_("Process record is stopped and all execution "
                       "logs are deleted.\n"));
 
   printf_unfiltered (_("Process record is stopped and all execution "
                       "logs are deleted.\n"));
 
-  observer_notify_record_changed (current_inferior (), 0);
+  observer_notify_record_changed (current_inferior (), 0, NULL, NULL);
 }
 
 /* The "set record" command.  */
 }
 
 /* The "set record" command.  */
@@ -268,8 +346,7 @@ info_record_command (char *args, int from_tty)
     }
 
   printf_filtered (_("Active record target: %s\n"), t->to_shortname);
     }
 
   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.  */
 }
 
 /* The "record save" command.  */
@@ -287,38 +364,62 @@ cmd_record_save (char *args, int from_tty)
     {
       /* Default recfile name is "gdb_record.PID".  */
       xsnprintf (recfilename_buffer, sizeof (recfilename_buffer),
     {
       /* 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);
 }
 
       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.  */
 
 /* "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)
 {
 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 ();
   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.  */
 }
 
 /* Read an instruction number from an argument string.  */
@@ -399,6 +500,7 @@ get_insn_history_modifiers (char **arg)
          switch (*args)
            {
            case 'm':
          switch (*args)
            {
            case 'm':
+           case 's':
              modifiers |= DISASSEMBLY_SOURCE;
              modifiers |= DISASSEMBLY_FILENAME;
              break;
              modifiers |= DISASSEMBLY_SOURCE;
              modifiers |= DISASSEMBLY_FILENAME;
              break;
@@ -408,6 +510,9 @@ get_insn_history_modifiers (char **arg)
            case 'f':
              modifiers |= DISASSEMBLY_OMIT_FNAME;
              break;
            case 'f':
              modifiers |= DISASSEMBLY_OMIT_FNAME;
              break;
+           case 'p':
+             modifiers |= DISASSEMBLY_OMIT_PC;
+             break;
            default:
              error (_("Invalid modifier: %c."), *args);
            }
            default:
              error (_("Invalid modifier: %c."), *args);
            }
@@ -422,6 +527,25 @@ get_insn_history_modifiers (char **arg)
   return modifiers;
 }
 
   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
 /* The "record instruction-history" command.  */
 
 static void
@@ -433,11 +557,7 @@ cmd_record_insn_history (char *arg, int from_tty)
 
   flags = get_insn_history_modifiers (&arg);
 
 
   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);
 
   if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
     target_insn_history (size, flags);
@@ -491,6 +611,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;
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_record;
 
@@ -508,11 +792,20 @@ _initialize_record (void)
                             &showdebuglist);
 
   add_setshow_uinteger_cmd ("instruction-history-size", no_class,
                             &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\"."), _("\
 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."),
 
   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
                      _("Start recording."),
@@ -551,16 +844,26 @@ Default filename is 'gdb_record.<process_id>'."),
            &record_cmdlist);
   add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
 
            &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'."),
 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\
 
   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 /m or /s 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 /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 \
 With no argument, disassembles ten more instructions after the previous \
 disassembly.\n\
 \"record instruction-history -\" disassembles ten instructions before a \
@@ -574,4 +877,28 @@ from the first argument.\n\
 The number of instructions to disassemble can be defined with \"set record \
 instruction-history-size\"."),
            &record_cmdlist);
 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;
 }
 }
This page took 0.03043 seconds and 4 git commands to generate.