This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 05/17] cli, btrace: add btrace cli


From: Markus Metzger <markus.t.metzger@intel.com>

Add branch trace commands:

  - "btrace enable/disable" perform the obvious operation

    ""     on the current thread.
    "all"  on each existing thread.
    "auto" on each newly created thread.

    Actually, "btrace enable auto" turns on automatic enabling for new threads,
    and "btrace disable auto" turns it off, again.

  - "btrace list" prints the blocks that have been traced.

    The output may be configured using modifiers. It prints:
      -  the block number
      /a the begin and end code address of that block
      /f the function containing the block
      /l the source lines contained in the block

    With the /t modifier, it prints the total number of blocks and exits.

    It accepts an optional range argument specifying the range of blocks to be
    listed. If no argument is given, all blocks are listed.

    The block number can be used to print the trace for one particular block or
    for a range of blocks.

  - "btrace" prints the branch trace disassembly for the current thread.

    Branch trace is printed block-by-block. Typically, one block at a time is
    printed.

    By default, the disassembly for the next block is printed, thus iterating
    over the full branch trace.

    The command supports the /m and /r modifiers accepted by the disassemble
    command.

    In addition, the command supports the following arguments:
      - "<n>"      set the iterator to the <n>-th block
      - "+[<n>]"   advance the iterator by <n>  (default: 1)
      - "-[<n>]"   advance the iterator by -<n> (default: 1)
      - "<l>-<h>"  set the iterator to the <h>'th block and
                   print the blocks in the range in reverse (i.e. original
                   control flow) order.

    Mixed source and disassembly does not work very well for inlined functions,
    a problem that it shares with the disassemble command.

2012-06-06 Markus Metzger <markus.t.metzger@intel.com>

gdb/
	* command.h (enum command_class): Add class_btrace.
	* btrace.c (thread_callback): New function.
	(for_each_thread): New function.
	(warn_enable_btrace): New function.
	(warn_disable_btrace): New function.
	(enable_btrace_list): New function.
	(cmd_btrace_enable): New function.
	(cmd_btrace_enable_all): New function.
	(cmd_btrace_enable_auto): New function.
	(disable_btrace_list): New function.
	(cmd_btrace_disable): New function.
	(cmd_btrace_disable_all): New function.
	(cmd_btrace_disable_auto): New function.
	(do_btrace_list_address): New function.
	(do_btrace_list_function): New function.
	(do_btrace_list_line): New function.
	(do_btrace_list): New function.
	(cmd_btrace_list): New function.
	(do_btrace): New function.
	(do_btrace_range): New function.
	(do_prev_btrace): New function.
	(do_next_btrace): New function.
	(cmd_btrace): New function.
	(_initialize_btrace): New function.


---
 gdb/btrace.c                    |  612 +++++++++++++++++++++++++++++++++++++++
 gdb/command.h                   |    2 +-
 gdb/testsuite/gdb.base/page.exp |    3 +-
 3 files changed, 615 insertions(+), 2 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 7b644f6..1c1202d 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -25,6 +25,28 @@
 #include "exceptions.h"
 #include "inferior.h"
 #include "target.h"
+#include "observer.h"
+#include "command.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-utils.h"
+#include "arch-utils.h"
+#include "disasm.h"
+#include "gdbarch.h"
+
+#include <ctype.h>
+#include <string.h>
+
+/* A new thread observer enabling branch tracing for the new thread.  */
+static struct observer *btrace_thread_observer;
+
+/* Branch tracing command list.  */
+static struct cmd_list_element *btcmdlist;
+
+/* Branch tracing enable command list.  */
+static struct cmd_list_element *btencmdlist;
+
+/* Branch tracing disable command list.  */
+static struct cmd_list_element *btdiscmdlist;
 
 /* Documentation see btrace.h.  */
 void
@@ -170,3 +192,593 @@ next_btrace (struct thread_info *tp)
 
   return VEC_index (btrace_block_s, btp->btrace, btp->iterator);
 }
+
+/* An iterate_over_threads callback to call a new thread observer function.  */
+static int
+thread_callback (struct thread_info *tp, void *arg)
+{
+  observer_new_thread_ftype *tfun = arg;
+
+  tfun (tp);
+
+  return 0;
+}
+
+/* Call @tfun for each thread.  */
+static void
+for_each_thread (observer_new_thread_ftype *tfun)
+{
+  iterate_over_threads (thread_callback, tfun);
+}
+
+/* Enable branch tracing. Turn errors into warnings.  */
+static void
+warn_enable_btrace (struct thread_info *tp)
+{
+  if (!target_supports_btrace ())
+    warning (_("Target does not support branch tracing."));
+  else
+    {
+      volatile struct gdb_exception error;
+
+      TRY_CATCH (error, RETURN_MASK_ERROR)
+	  enable_btrace (tp);
+
+      if (error.message)
+	warning (_("%s"), error.message);
+    }
+}
+
+/* Disable branch tracing. Turn errors into warnings.  */
+static void
+warn_disable_btrace (struct thread_info *tp)
+{
+  if (!target_supports_btrace ())
+    warning (_("Target does not support branch tracing."));
+  else
+    {
+      volatile struct gdb_exception error;
+
+      TRY_CATCH (error, RETURN_MASK_ERROR)
+	disable_btrace (tp);
+
+      if (error.message)
+	warning (_("%s"), error.message);
+    }
+}
+
+/* Thread iteration callback to enable branch tracing if @tp is in the @arg
+   range of thread nums.  */
+static int
+enable_btrace_list (struct thread_info *tp, void *arg)
+{
+  char *range = (char *)arg;
+
+  if (number_is_in_list (range, tp->num))
+    warn_enable_btrace (tp);
+
+  return 0;
+}
+
+/* The "btrace enable" command.  */
+static void
+cmd_btrace_enable (char *args, int from_tty)
+{
+  if (args && *args)
+    iterate_over_threads (enable_btrace_list, args);
+  else
+    {
+      struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+      if (!tp)
+	error (_("Couldn't enable branch tracing: no inferior thread."));
+
+      enable_btrace (tp);
+    }
+}
+
+/* The "btrace enable all" command.  */
+static void
+cmd_btrace_enable_all (char *args, int from_tty)
+{
+  if (args && *args)
+    error (_("Invalid argument."));
+
+  for_each_thread (warn_enable_btrace);
+}
+
+/* The "btrace enable auto" command.  */
+static void
+cmd_btrace_enable_auto (char *args, int from_tty)
+{
+  if (args && *args)
+    error (_("Invalid argument."));
+
+  if (btrace_thread_observer)
+    error (_("Automatic branch trace enabling already on."));
+
+  btrace_thread_observer =
+    observer_attach_new_thread (warn_enable_btrace);
+}
+
+/* Thread iteration callback to disable branch tracing if @tp is in the @arg
+   range of thread nums.  */
+static int
+disable_btrace_list (struct thread_info *tp, void *arg)
+{
+  char *range = (char *)arg;
+
+  if (number_is_in_list (range, tp->num))
+    warn_disable_btrace (tp);
+
+  return 0;
+}
+
+/* The "btrace disable" command.  */
+static void
+cmd_btrace_disable (char *args, int from_tty)
+{
+  if (args && *args)
+    iterate_over_threads (disable_btrace_list, args);
+  else
+    {
+      struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+      if (!tp)
+	error (_("Couldn't disable branch tracing: no inferior thread."));
+
+      disable_btrace (tp);
+    }
+}
+
+/* The "btrace disable all" command.  */
+static void
+cmd_btrace_disable_all (char *args, int from_tty)
+{
+  if (args && *args)
+    error (_("Invalid argument."));
+
+  for_each_thread (warn_disable_btrace);
+}
+
+/* The "btrace disable auto" command.  */
+static void
+cmd_btrace_disable_auto (char *args, int from_tty)
+{
+  if (args && *args)
+    error (_("Invalid argument."));
+
+  if (!btrace_thread_observer)
+    error (_("Automatic branch trace enabling already off."));
+
+  observer_detach_new_thread (btrace_thread_observer);
+  btrace_thread_observer = NULL;
+}
+
+/* Flags controlling the output of a btrace list command.  */
+enum btrace_list_flags
+{
+  /* Print code addresses.  */
+  BTR_LIST_ADDRESS  = (1 << 0),
+
+  /* Print function names.  */
+  BTR_LIST_FUNCTION = (1 << 1),
+
+  /* Print line information.  */
+  BTR_LIST_LINE     = (1 << 2),
+
+  /* Print the total number of btrace blocks.  */
+  BTR_LIST_TOTAL    = (1 << 3)
+};
+
+/* Print the addresses for the btrace list item @trace.  */
+static void
+do_btrace_list_address (struct btrace_block *trace)
+{
+  struct gdbarch *gdbarch = target_gdbarch;
+
+  ui_out_field_core_addr (current_uiout, "begin", gdbarch, trace->begin);
+  ui_out_text (current_uiout, " - ");
+  ui_out_field_core_addr (current_uiout, "end", gdbarch, trace->end);
+}
+
+/* Print the function containing the btrace list item @trace.  */
+static void
+do_btrace_list_function (struct btrace_block *trace)
+{
+  struct symbol *symbol = find_pc_function (trace->begin);
+
+  ui_out_text (current_uiout, " in ");
+  if (symbol)
+    ui_out_field_string (current_uiout, "function",
+			 SYMBOL_PRINT_NAME (symbol));
+  else
+    {
+      struct minimal_symbol *msymbol =
+	lookup_minimal_symbol_by_pc (trace->begin);
+
+      if (msymbol)
+	ui_out_field_string (current_uiout, "function",
+			     SYMBOL_PRINT_NAME (msymbol));
+      else
+	ui_out_text (current_uiout, "??");
+    }
+
+  ui_out_text (current_uiout, " ()");
+}
+
+/* Print the line information for the btrace list item @trace.  */
+static void
+do_btrace_list_line (struct btrace_block *trace)
+{
+  struct symbol *symbol = find_pc_function (trace->begin);
+  struct symtab *symtab = NULL;
+  struct linetable *linetable = NULL;
+  const char *filename = NULL;
+
+  if (symbol)
+    symtab = symbol->symtab;
+  if (!symtab)
+    symtab = find_pc_symtab (trace->begin);
+  if (symtab)
+    {
+      linetable = symtab->linetable;
+      filename = symtab->filename;
+    }
+
+  if (filename)
+    {
+      ui_out_text (current_uiout, " at ");
+      ui_out_field_string (current_uiout, "file", filename);
+
+      if (linetable)
+	{
+	  struct linetable_entry *line = linetable->item;
+	  int nitems = linetable->nitems, i = 0;
+	  int begin = INT_MAX, end = 0;
+
+	  /* Skip all preceding entries.  */
+	  for (; i < (nitems - 1) && line[i + 1].pc <= trace->begin; ++i);
+
+	  for (; i < nitems && line[i].pc <= trace->end; ++i)
+	    {
+	      begin = min (begin, line[i].line);
+	      end = max (end, line[i].line);
+	    }
+
+	  if (begin <= end)
+	    {
+	      ui_out_text (current_uiout, ":");
+	      ui_out_field_int (current_uiout, "lbegin", begin);
+
+	      if (begin < end)
+		{
+		  ui_out_text (current_uiout, "-");
+		  ui_out_field_int (current_uiout, "lend", end);
+		}
+	    }
+	}
+    }
+}
+
+/* Print the btrace list item @trace based on @flags.  */
+static void
+do_btrace_list_item (struct btrace_block *trace, enum btrace_list_flags flags)
+{
+  if (flags & BTR_LIST_ADDRESS)
+    do_btrace_list_address (trace);
+
+  if (flags & BTR_LIST_FUNCTION)
+    do_btrace_list_function (trace);
+
+  if (flags & BTR_LIST_LINE)
+    do_btrace_list_line (trace);
+}
+
+/* Print the btrace list for a range of blocks.  */
+static void
+do_btrace_list (VEC (btrace_block_s) *btrace, char *range,
+		enum btrace_list_flags flags)
+{
+  struct cleanup *ui_out_chain =
+    make_cleanup_ui_out_tuple_begin_end (current_uiout, "btrace blocks");
+  unsigned int block = 0;
+  struct btrace_block *trace;
+
+  while (VEC_iterate (btrace_block_s, btrace, block, trace))
+    {
+      ++block;
+
+      if (number_is_in_list (range, block))
+	{
+	  ui_out_field_fmt_int (current_uiout, 5, ui_left, "block", block);
+	  do_btrace_list_item (trace, flags);
+	  ui_out_text (current_uiout, "\n");
+	}
+    }
+
+  do_cleanups (ui_out_chain);
+}
+
+/* The "btrace list" command.  */
+static void
+cmd_btrace_list (char *args, int from_tty)
+{
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  enum btrace_list_flags flags = BTR_LIST_FUNCTION | BTR_LIST_LINE;
+  VEC (btrace_block_s) *btrace;
+
+  if (!tp)
+    error (_("No thread."));
+
+  btrace = get_btrace (tp);
+
+  /* Parse modifier.  */
+  if (args && *args)
+    {
+      if (*args == '/')
+	flags = 0;
+
+      while (*args == '/')
+	{
+	  ++args;
+
+	  if (*args == '\0')
+	    error (_("Missing modifier."));
+
+	  for (; *args; ++args)
+	    {
+	      if (isspace (*args))
+		break;
+
+	      if (*args == '/')
+		continue;
+
+	      switch (*args)
+		{
+		case 'a':
+		  flags |= BTR_LIST_ADDRESS;
+		  break;
+		case 'f':
+		  flags |= BTR_LIST_FUNCTION;
+		  break;
+		case 'l':
+		  flags |= BTR_LIST_LINE;
+		  break;
+		case 't':
+		  flags |= BTR_LIST_TOTAL;
+		  break;
+		default:
+		  error (_("Invalid modifier: %c."), *args);
+		}
+	    }
+
+	  args = skip_spaces (args);
+	}
+    }
+
+  if (flags & BTR_LIST_TOTAL)
+    {
+      struct cleanup *ui_out_chain =
+	make_cleanup_ui_out_tuple_begin_end (current_uiout, "btrace blocks");
+      unsigned int total = VEC_length (btrace_block_s, btrace);
+
+      ui_out_text (current_uiout, "total: ");
+      ui_out_field_int (current_uiout, "total", total);
+      ui_out_text (current_uiout, "\n");
+
+      do_cleanups (ui_out_chain);
+    }
+  else if (btrace)
+    do_btrace_list (btrace, args, flags);
+  else
+    error (_("No trace."));
+}
+
+/* Print the disassembly of @trace based on @flags.  */
+static void
+do_btrace (struct btrace_block *trace, int flags)
+{
+  struct gdbarch *gdbarch = target_gdbarch;
+
+  if (!trace)
+    error (_("No trace."));
+
+  if (trace->end < trace->begin)
+    warning (_("Bad trace: %s - %s"), paddress (gdbarch, trace->begin),
+	     paddress (gdbarch, trace->end));
+
+  gdb_disassembly (gdbarch, current_uiout, 0, flags, -1,
+		   trace->begin, trace->end + 1);
+}
+
+/* Print the branch trace disassembly for a range of btrace blocks.  */
+static void
+do_btrace_range (struct thread_info *tp, int flags, char *args)
+{
+  struct get_number_or_range_state state;
+  unsigned int i;
+  VEC (btrace_block_s) *btrace = NULL;
+  struct cleanup *cleanup =
+    make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
+
+  /* We store the relevant blocks into a separate vector, so we can display
+     them in reverse order.  */
+  init_number_or_range (&state, args);
+  while (!state.finished)
+    {
+      int index = get_number_or_range (&state);
+      struct btrace_block *trace;
+
+      if (!index)
+	error (_("Args must be numbers or '$' variables."));
+
+      trace = read_btrace (tp, index - 1);
+      if (!trace)
+	continue;
+
+      VEC_safe_push (btrace_block_s, btrace, trace);
+    }
+
+  i = VEC_length (btrace_block_s, btrace);
+  while (i--)
+    do_btrace (VEC_index (btrace_block_s, btrace, i), flags);
+
+  do_cleanups (cleanup);
+}
+
+/* Print the branch trace disassembly for a preceding block.  */
+static void
+do_prev_btrace (struct thread_info *tp, int flags, char *args)
+{
+  struct btrace_block *trace = NULL;
+  size_t skip = 1;
+
+  if (args[0])
+    skip = get_number (&args);
+
+  while (skip--)
+    trace = prev_btrace (tp);
+
+  if (args[0])
+    error (_("Junk after argument: %s"), args);
+
+  do_btrace (trace, flags);
+}
+
+/* Print the branch trace disassembly for a succeding block.  */
+static void
+do_next_btrace (struct thread_info *tp, int flags, char *args)
+{
+  struct btrace_block *trace = NULL;
+  size_t skip = 1;
+
+  if (args[0])
+    skip = get_number (&args);
+
+  while (skip--)
+    trace = next_btrace (tp);
+
+  if (args[0])
+    error (_("Junk after argument: %s"), args);
+
+  do_btrace (trace, flags);
+}
+
+/* The "btrace" command.  */
+static void
+cmd_btrace (char *args, int from_tty)
+{
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  struct btrace_block *trace = NULL;
+  int flags = 0;
+
+  if (!tp)
+    error (_("No thread."));
+
+  /* Parse modifier.  We accept the same modifiers as the disas command.  */
+  if (args && *args)
+    {
+      while (*args == '/')
+	{
+	  ++args;
+
+	  if (*args == '\0')
+	    error (_("Missing modifier."));
+
+	  for (; *args; ++args)
+	    {
+	      if (isspace (*args))
+		break;
+
+	      if (*args == '/')
+		continue;
+
+	      switch (*args)
+		{
+		case 'm':
+		  flags |= DISASSEMBLY_SOURCE;
+		  flags |= DISASSEMBLY_PRECISE_INSN;
+		  flags |= DISASSEMBLY_FILENAME;
+		  break;
+		case 'r':
+		  flags |= DISASSEMBLY_RAW_INSN;
+		  break;
+		default:
+		  error (_("Invalid modifier: %c."), *args);
+		}
+	    }
+
+	  args = skip_spaces (args);
+	}
+    }
+
+  if (!args || !*args)
+    do_btrace (prev_btrace (tp), flags);
+  else if (args[0] == '+')
+    do_prev_btrace (tp, flags, args+1);
+  else if (args[0] == '-')
+    do_next_btrace (tp, flags, args+1);
+  else
+    do_btrace_range (tp, flags, args);
+}
+
+void _initialize_btrace (void);
+
+/* Initialize btrace commands.  */
+void
+_initialize_btrace (void)
+{
+  struct cmd_list_element *c;
+
+  add_cmd ("branchtrace", class_btrace, NULL, _("Recording a branch trace."),
+	   &cmdlist);
+
+  add_prefix_cmd ("btrace", class_btrace, cmd_btrace, _("\
+Disassemble the selected branch trace block.\n\n\
+With a /m modifier, source lines are included (if available).\n\
+With a /r modifier, raw instructions in hex are included.\n\n\
+Without arguments, selects the chronologically preceding block.\n\
+With \"+[<n>]\" argument, selects the n-th chronologically preceding block.\n\
+With \"-[<n>]\" argument, selects the n-th chronologically succeeding block.\n\
+With one positive integer argument, selects the respective block.\n\
+With a range argument \"<l>-<h>\", selects the h-th block and disassembles \
+blocks in the range in reverse (i.e. original control flow) order.\n"),
+		  &btcmdlist, "btrace ", 1, &cmdlist);
+
+  add_prefix_cmd ("enable", class_btrace, cmd_btrace_enable, _("\
+Enable branch trace recording for the current thread.\n"),
+		  &btencmdlist, "btrace enable ", 1, &btcmdlist);
+
+  add_cmd ("all", class_btrace, cmd_btrace_enable_all, _("\
+Enable branch trace recording for all existing threads.\n"),
+	   &btencmdlist);
+
+  add_cmd ("auto", class_btrace, cmd_btrace_enable_auto, _("\
+Enable branch trace recording for each new thread.\n"),
+	   &btencmdlist);
+
+  add_prefix_cmd ("disable", class_btrace, cmd_btrace_disable, _("\
+Disable branch trace recording for the current thread.\n"),
+		  &btdiscmdlist, "btrace disable ", 1, &btcmdlist);
+
+  add_cmd ("all", class_btrace, cmd_btrace_disable_all, _("\
+Disable branch trace recording for all existing threads.\n"),
+	   &btdiscmdlist);
+
+  add_cmd ("auto", class_btrace, cmd_btrace_disable_auto, _("\
+Stop enabling branch trace recording for each new thread.\n"),
+	   &btdiscmdlist);
+
+  add_cmd ("list", class_btrace, cmd_btrace_list, _("\
+List branch trace blocks.\n\n\
+Prints a list of all blocks for which branch trace is available.\n\
+With a /a modifier, addresses are included.\n\
+With a /f modifier, the function name is included (if available).\n\
+With a /l modifier, source lines are include (if available).\n\
+With a /t modifier, prints the total number of trace blocks and stops.\n\
+Without any modifier, behaves as if /fl were specified.\n\n\
+Without arguments, the full list of blocks is listed.\n\
+With a range (<start>[-<end>]) argument, lists all blocks in that range.\n"),
+	   &btcmdlist);
+}
diff --git a/gdb/command.h b/gdb/command.h
index c18e2dd..999ec1d 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -37,7 +37,7 @@ enum command_class
   no_class = -1, class_run = 0, class_vars, class_stack, class_files,
   class_support, class_info, class_breakpoint, class_trace,
   class_alias, class_bookmark, class_obscure, class_maintenance,
-  class_pseudo, class_tui, class_user, class_xdb,
+  class_pseudo, class_tui, class_user, class_xdb, class_btrace,
   no_set_class	/* Used for "show" commands that have no corresponding
 		   "set" command.  */
 };
diff --git a/gdb/testsuite/gdb.base/page.exp b/gdb/testsuite/gdb.base/page.exp
index 0629807..d9b3899 100644
--- a/gdb/testsuite/gdb.base/page.exp
+++ b/gdb/testsuite/gdb.base/page.exp
@@ -25,6 +25,7 @@ gdb_test_sequence "help" "unpaged help" {
     "List of classes of commands:"
     ""
     "aliases -- Aliases of other commands"
+    "branchtrace -- Recording a branch trace"
     "breakpoints -- Making program stop at certain points"
     "data -- Examining data"
     "files -- Specifying and examining files"
@@ -52,12 +53,12 @@ gdb_expect_list "paged help" \
     "List of classes of commands:"
     ""
     "aliases -- Aliases of other commands"
+    "branchtrace -- Recording a branch trace"
     "breakpoints -- Making program stop at certain points"
     "data -- Examining data"
     "files -- Specifying and examining files"
     "internals -- Maintenance commands"
     "obscure -- Obscure features"
-    "running -- Running the program"
 }
 gdb_test "q"
 
-- 
1.7.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]