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 v6 02/12] 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-12-17 Markus Metzger <markus.t.metzger@intel.com>

	* command.h (enum command_class): Add class_btrace.
	* btrace.c (warn_enable_btrace): New function.
	(warn_disable_btrace): New function.
	(cmd_btrace_enable): New function.
	(cmd_btrace_enable_all): New function.
	(cmd_btrace_enable_auto): 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_item): 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                    |  600 +++++++++++++++++++++++++++++++++++++++
 gdb/command.h                   |    2 +-
 gdb/testsuite/gdb.base/page.exp |    3 +-
 3 files changed, 603 insertions(+), 2 deletions(-)

diff --git a/gdb/btrace.c b/gdb/btrace.c
index 667a738..488fd09 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;
 
 /* See btrace.h.  */
 
@@ -166,3 +188,581 @@ next_btrace (struct thread_info *tp)
 
   return VEC_index (btrace_block_s, btp->btrace, btp->iterator);
 }
+
+/* Enable branch tracing. Turn errors into warnings.  */
+
+static void
+warn_enable_btrace (struct thread_info *tp)
+{
+  volatile struct gdb_exception error;
+
+  TRY_CATCH (error, RETURN_MASK_ERROR)
+    enable_btrace (tp);
+
+  if (error.message != NULL)
+    warning (_("%s"), error.message);
+}
+
+/* Disable branch tracing. Turn errors into warnings.  */
+
+static void
+warn_disable_btrace (struct thread_info *tp)
+{
+  volatile struct gdb_exception error;
+
+  TRY_CATCH (error, RETURN_MASK_ERROR)
+    disable_btrace (tp);
+
+  if (error.message != NULL)
+    warning (_("%s"), error.message);
+}
+
+/* The "btrace enable" command.  */
+
+static void
+cmd_btrace_enable (char *args, int from_tty)
+{
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    {
+      ALL_THREADS (tp)
+	if (number_is_in_list (args, tp->num))
+	  warn_enable_btrace (tp);
+    }
+  else
+    {
+      tp = find_thread_ptid (inferior_ptid);
+      if (tp == NULL)
+	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)
+{
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  ALL_THREADS (tp)
+    warn_enable_btrace (tp);
+}
+
+/* The "btrace enable auto" command.  */
+
+static void
+cmd_btrace_enable_auto (char *args, int from_tty)
+{
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  if (btrace_thread_observer != NULL)
+    return;
+
+  btrace_thread_observer
+    = observer_attach_new_thread (warn_enable_btrace);
+}
+
+/* The "btrace disable" command.  */
+
+static void
+cmd_btrace_disable (char *args, int from_tty)
+{
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    {
+      ALL_THREADS (tp)
+	if (number_is_in_list (args, tp->num))
+	  warn_enable_btrace (tp);
+    }
+  else
+    {
+      tp = find_thread_ptid (inferior_ptid);
+      if (tp == NULL)
+	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)
+{
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  ALL_THREADS (tp)
+    warn_disable_btrace (tp);
+}
+
+/* The "btrace disable auto" command.  */
+
+static void
+cmd_btrace_disable_auto (char *args, int from_tty)
+{
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  if (btrace_thread_observer == NULL)
+    return;
+
+  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 a btrace block.  */
+
+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 a btrace block.  */
+
+static void
+do_btrace_list_function (struct btrace_block *trace)
+{
+  struct minimal_symbol *msymbol;
+  struct symbol *symbol;
+  const char* func;
+
+  func = "??";
+  symbol = find_pc_function (trace->begin);
+  if (symbol != NULL)
+    func = SYMBOL_PRINT_NAME (symbol);
+  else
+    {
+      msymbol = lookup_minimal_symbol_by_pc (trace->begin);
+      if (msymbol != NULL)
+	func = SYMBOL_PRINT_NAME (msymbol);
+    }
+
+  ui_out_text (current_uiout, " in ");
+  ui_out_field_string (current_uiout, "function", func);
+}
+
+/* Print the line information for a btrace block.  */
+
+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 != NULL)
+    symtab = symbol->symtab;
+  if (symtab == NULL)
+    symtab = find_pc_symtab (trace->begin);
+  if (symtab != NULL)
+    {
+      linetable = symtab->linetable;
+      filename = symtab->filename;
+    }
+
+  if (filename != NULL)
+    {
+      ui_out_text (current_uiout, " at ");
+      ui_out_field_string (current_uiout, "file", filename);
+
+      if (linetable != NULL)
+	{
+	  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 a btrace block.  */
+
+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;
+  unsigned int block;
+  struct btrace_block *trace;
+
+  ui_out_chain
+    = make_cleanup_ui_out_tuple_begin_end (current_uiout, "btrace blocks");
+  block = 0;
+
+  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 == NULL)
+    error (_("No thread."));
+
+  btrace = get_btrace (tp);
+
+  /* Parse modifier.  */
+  if (args != NULL && *args != 0)
+    {
+      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;
+      unsigned int total;
+
+      total = VEC_length (btrace_block_s, btrace);
+      ui_out_chain
+	= make_cleanup_ui_out_tuple_begin_end (current_uiout, "btrace blocks");
+
+      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 != NULL)
+    do_btrace_list (btrace, args, flags);
+  else
+    error (_("No trace."));
+}
+
+/* Print the disassembly of a btrace block.  */
+
+static void
+do_btrace (struct btrace_block *trace, int flags)
+{
+  struct gdbarch *gdbarch = target_gdbarch ();
+
+  if (trace == NULL)
+    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;
+  struct cleanup *cleanup;
+
+  btrace = NULL;
+  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)
+    {
+      struct btrace_block *trace;
+      int index;
+
+      index = get_number_or_range (&state);
+      if (index == 0)
+	error (_("Args must be numbers or '$' variables."));
+
+      trace = read_btrace (tp, index - 1);
+      if (trace == NULL)
+	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] != 0)
+    skip = get_number (&args);
+
+  while (skip-- != 0)
+    trace = prev_btrace (tp);
+
+  if (args[0] != 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] != 0)
+    skip = get_number (&args);
+
+  while (skip-- != 0)
+    trace = next_btrace (tp);
+
+  if (args[0] != 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 == NULL)
+    error (_("No thread."));
+
+  /* Parse modifier.  We accept the same modifiers as the disas command.  */
+  if (args != NULL && *args != 0)
+    {
+      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_FILENAME;
+		  break;
+		case 'r':
+		  flags |= DISASSEMBLY_RAW_INSN;
+		  break;
+		default:
+		  error (_("Invalid modifier: %c."), *args);
+		}
+	    }
+
+	  args = skip_spaces (args);
+	}
+    }
+
+  if (args == NULL || *args == 0)
+    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, include source lines (if available).\n\
+With a /r modifier, include raw instructions in hex.\n\n\
+Without arguments, select the chronologically preceding block.\n\
+With \"+[<n>]\" argument, select the n-th chronologically preceding block.\n\
+With \"-[<n>]\" argument, select the n-th chronologically succeeding block.\n\
+With one positive integer argument, select the respective block.\n\
+With a range argument \"<l>-<h>\", select the h-th block and disassemble \
+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 new threads.\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 new threads.\n"),
+	   &btdiscmdlist);
+
+  add_cmd ("list", class_btrace, cmd_btrace_list, _("\
+List branch trace blocks.\n\n\
+Print a list of all blocks for which branch trace is available.\n\
+With a /a modifier, include addresses.\n\
+With a /f modifier, include the function name (if available).\n\
+With a /l modifier, include source lines (if available).\n\
+With a /t modifier, print the total number of trace blocks and stop.\n\
+By default, include the function name and source lines.\n\n\
+Without arguments, list the full list of blocks.\n\
+With a range (<start>[-<end>]) argument, list all blocks in that range.\n"),
+	   &btcmdlist);
+}
diff --git a/gdb/command.h b/gdb/command.h
index 8eb86ba..b576b70 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -39,7 +39,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.6.5


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