Improve MSP430 section placement.
[deliverable/binutils-gdb.git] / gdb / btrace.c
index 8930c3cf6a90552ac0acfb4e422eb3f195978d1f..2a83e1b26f0f8622d308fd143fc87794cb6568b6 100644 (file)
@@ -1,6 +1,6 @@
 /* Branch trace support for GDB, the GNU debugger.
 
-   Copyright (C) 2013-2015 Free Software Foundation, Inc.
+   Copyright (C) 2013-2017 Free Software Foundation, Inc.
 
    Contributed by Intel Corp. <markus.t.metzger@intel.com>
 
 #include "filenames.h"
 #include "xml-support.h"
 #include "regcache.h"
+#include "rsp-low.h"
+#include "gdbcmd.h"
+#include "cli/cli-utils.h"
+
+#include <inttypes.h>
+#include <ctype.h>
+#include <algorithm>
+
+/* Command lists for btrace maintenance commands.  */
+static struct cmd_list_element *maint_btrace_cmdlist;
+static struct cmd_list_element *maint_btrace_set_cmdlist;
+static struct cmd_list_element *maint_btrace_show_cmdlist;
+static struct cmd_list_element *maint_btrace_pt_set_cmdlist;
+static struct cmd_list_element *maint_btrace_pt_show_cmdlist;
+
+/* Control whether to skip PAD packets when computing the packet history.  */
+static int maint_btrace_pt_skip_pad = 1;
+
+static void btrace_add_pc (struct thread_info *tp);
 
 /* Print a record debug message.  Use do ... while (0) to avoid ambiguities
    when used in if statements.  */
@@ -105,21 +124,57 @@ ftrace_debug (const struct btrace_function *bfun, const char *prefix)
 {
   const char *fun, *file;
   unsigned int ibegin, iend;
-  int lbegin, lend, level;
+  int level;
 
   fun = ftrace_print_function_name (bfun);
   file = ftrace_print_filename (bfun);
   level = bfun->level;
 
-  lbegin = bfun->lbegin;
-  lend = bfun->lend;
-
   ibegin = bfun->insn_offset;
   iend = ibegin + VEC_length (btrace_insn_s, bfun->insn);
 
-  DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, lines = [%d; %d], "
-               "insn = [%u; %u)", prefix, fun, file, level, lbegin, lend,
-               ibegin, iend);
+  DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, insn = [%u; %u)",
+               prefix, fun, file, level, ibegin, iend);
+}
+
+/* Return the number of instructions in a given function call segment.  */
+
+static unsigned int
+ftrace_call_num_insn (const struct btrace_function* bfun)
+{
+  if (bfun == NULL)
+    return 0;
+
+  /* A gap is always counted as one instruction.  */
+  if (bfun->errcode != 0)
+    return 1;
+
+  return VEC_length (btrace_insn_s, bfun->insn);
+}
+
+/* Return the function segment with the given NUMBER or NULL if no such segment
+   exists.  BTINFO is the branch trace information for the current thread.  */
+
+static struct btrace_function *
+ftrace_find_call_by_number (struct btrace_thread_info *btinfo,
+                           unsigned int number)
+{
+  if (number == 0 || number > btinfo->functions.size ())
+    return NULL;
+
+  return &btinfo->functions[number - 1];
+}
+
+/* A const version of the function above.  */
+
+static const struct btrace_function *
+ftrace_find_call_by_number (const struct btrace_thread_info *btinfo,
+                           unsigned int number)
+{
+  if (number == 0 || number > btinfo->functions.size ())
+    return NULL;
+
+  return &btinfo->functions[number - 1];
 }
 
 /* Return non-zero if BFUN does not match MFUN and FUN,
@@ -168,64 +223,37 @@ ftrace_function_switched (const struct btrace_function *bfun,
   return 0;
 }
 
-/* Return non-zero if we should skip this file when generating the function
-   call history, zero otherwise.
-   We would want to do that if, say, a macro that is defined in another file
-   is expanded in this function.  */
-
-static int
-ftrace_skip_file (const struct btrace_function *bfun, const char *fullname)
-{
-  struct symbol *sym;
-  const char *bfile;
-
-  sym = bfun->sym;
-  if (sym == NULL)
-    return 1;
-
-  bfile = symtab_to_fullname (symbol_symtab (sym));
-
-  return (filename_cmp (bfile, fullname) != 0);
-}
-
-/* Allocate and initialize a new branch trace function segment.
-   PREV is the chronologically preceding function segment.
-   MFUN and FUN are the symbol information we have for this function.  */
+/* Allocate and initialize a new branch trace function segment at the end of
+   the trace.
+   BTINFO is the branch trace information for the current thread.
+   MFUN and FUN are the symbol information we have for this function.
+   This invalidates all struct btrace_function pointer currently held.  */
 
 static struct btrace_function *
-ftrace_new_function (struct btrace_function *prev,
+ftrace_new_function (struct btrace_thread_info *btinfo,
                     struct minimal_symbol *mfun,
                     struct symbol *fun)
 {
-  struct btrace_function *bfun;
-
-  bfun = xzalloc (sizeof (*bfun));
-
-  bfun->msym = mfun;
-  bfun->sym = fun;
-  bfun->flow.prev = prev;
-
-  /* We start with the identities of min and max, respectively.  */
-  bfun->lbegin = INT_MAX;
-  bfun->lend = INT_MIN;
+  int level;
+  unsigned int number, insn_offset;
 
-  if (prev == NULL)
+  if (btinfo->functions.empty ())
     {
-      /* Start counting at one.  */
-      bfun->number = 1;
-      bfun->insn_offset = 1;
+      /* Start counting NUMBER and INSN_OFFSET at one.  */
+      level = 0;
+      number = 1;
+      insn_offset = 1;
     }
   else
     {
-      gdb_assert (prev->flow.next == NULL);
-      prev->flow.next = bfun;
-
-      bfun->number = prev->number + 1;
-      bfun->insn_offset = (prev->insn_offset
-                          + VEC_length (btrace_insn_s, prev->insn));
+      const struct btrace_function *prev = &btinfo->functions.back ();
+      level = prev->level;
+      number = prev->number + 1;
+      insn_offset = prev->insn_offset + ftrace_call_num_insn (prev);
     }
 
-  return bfun;
+  btinfo->functions.emplace_back (mfun, fun, number, insn_offset, level);
+  return &btinfo->functions.back ();
 }
 
 /* Update the UP field of a function segment.  */
@@ -235,68 +263,78 @@ ftrace_update_caller (struct btrace_function *bfun,
                      struct btrace_function *caller,
                      enum btrace_function_flag flags)
 {
-  if (bfun->up != NULL)
+  if (bfun->up != 0)
     ftrace_debug (bfun, "updating caller");
 
-  bfun->up = caller;
+  bfun->up = caller->number;
   bfun->flags = flags;
 
   ftrace_debug (bfun, "set caller");
+  ftrace_debug (caller, "..to");
 }
 
 /* Fix up the caller for all segments of a function.  */
 
 static void
-ftrace_fixup_caller (struct btrace_function *bfun,
+ftrace_fixup_caller (struct btrace_thread_info *btinfo,
+                    struct btrace_function *bfun,
                     struct btrace_function *caller,
                     enum btrace_function_flag flags)
 {
-  struct btrace_function *prev, *next;
+  unsigned int prev, next;
 
+  prev = bfun->prev;
+  next = bfun->next;
   ftrace_update_caller (bfun, caller, flags);
 
   /* Update all function segments belonging to the same function.  */
-  for (prev = bfun->segment.prev; prev != NULL; prev = prev->segment.prev)
-    ftrace_update_caller (prev, caller, flags);
+  for (; prev != 0; prev = bfun->prev)
+    {
+      bfun = ftrace_find_call_by_number (btinfo, prev);
+      ftrace_update_caller (bfun, caller, flags);
+    }
 
-  for (next = bfun->segment.next; next != NULL; next = next->segment.next)
-    ftrace_update_caller (next, caller, flags);
+  for (; next != 0; next = bfun->next)
+    {
+      bfun = ftrace_find_call_by_number (btinfo, next);
+      ftrace_update_caller (bfun, caller, flags);
+    }
 }
 
-/* Add a new function segment for a call.
-   CALLER is the chronologically preceding function segment.
+/* Add a new function segment for a call at the end of the trace.
+   BTINFO is the branch trace information for the current thread.
    MFUN and FUN are the symbol information we have for this function.  */
 
 static struct btrace_function *
-ftrace_new_call (struct btrace_function *caller,
+ftrace_new_call (struct btrace_thread_info *btinfo,
                 struct minimal_symbol *mfun,
                 struct symbol *fun)
 {
-  struct btrace_function *bfun;
+  const unsigned int length = btinfo->functions.size ();
+  struct btrace_function *bfun = ftrace_new_function (btinfo, mfun, fun);
 
-  bfun = ftrace_new_function (caller, mfun, fun);
-  bfun->up = caller;
-  bfun->level = caller->level + 1;
+  bfun->up = length;
+  bfun->level += 1;
 
   ftrace_debug (bfun, "new call");
 
   return bfun;
 }
 
-/* Add a new function segment for a tail call.
-   CALLER is the chronologically preceding function segment.
+/* Add a new function segment for a tail call at the end of the trace.
+   BTINFO is the branch trace information for the current thread.
    MFUN and FUN are the symbol information we have for this function.  */
 
 static struct btrace_function *
-ftrace_new_tailcall (struct btrace_function *caller,
+ftrace_new_tailcall (struct btrace_thread_info *btinfo,
                     struct minimal_symbol *mfun,
                     struct symbol *fun)
 {
-  struct btrace_function *bfun;
+  const unsigned int length = btinfo->functions.size ();
+  struct btrace_function *bfun = ftrace_new_function (btinfo, mfun, fun);
 
-  bfun = ftrace_new_function (caller, mfun, fun);
-  bfun->up = caller;
-  bfun->level = caller->level + 1;
+  bfun->up = length;
+  bfun->level += 1;
   bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
 
   ftrace_debug (bfun, "new tail call");
@@ -304,15 +342,31 @@ ftrace_new_tailcall (struct btrace_function *caller,
   return bfun;
 }
 
+/* Return the caller of BFUN or NULL if there is none.  This function skips
+   tail calls in the call chain.  BTINFO is the branch trace information for
+   the current thread.  */
+static struct btrace_function *
+ftrace_get_caller (struct btrace_thread_info *btinfo,
+                  struct btrace_function *bfun)
+{
+  for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
+    if ((bfun->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
+      return ftrace_find_call_by_number (btinfo, bfun->up);
+
+  return NULL;
+}
+
 /* Find the innermost caller in the back trace of BFUN with MFUN/FUN
-   symbol information.  */
+   symbol information.  BTINFO is the branch trace information for the current
+   thread.  */
 
 static struct btrace_function *
-ftrace_find_caller (struct btrace_function *bfun,
+ftrace_find_caller (struct btrace_thread_info *btinfo,
+                   struct btrace_function *bfun,
                    struct minimal_symbol *mfun,
                    struct symbol *fun)
 {
-  for (; bfun != NULL; bfun = bfun->up)
+  for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
     {
       /* Skip functions with incompatible symbol information.  */
       if (ftrace_function_switched (bfun, mfun, fun))
@@ -327,54 +381,57 @@ ftrace_find_caller (struct btrace_function *bfun,
 
 /* Find the innermost caller in the back trace of BFUN, skipping all
    function segments that do not end with a call instruction (e.g.
-   tail calls ending with a jump).  */
+   tail calls ending with a jump).  BTINFO is the branch trace information for
+   the current thread.  */
 
 static struct btrace_function *
-ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
+ftrace_find_call (struct btrace_thread_info *btinfo,
+                 struct btrace_function *bfun)
 {
-  for (; bfun != NULL; bfun = bfun->up)
+  for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
     {
       struct btrace_insn *last;
-      CORE_ADDR pc;
 
-      /* We do not allow empty function segments.  */
-      gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn));
+      /* Skip gaps.  */
+      if (bfun->errcode != 0)
+       continue;
 
       last = VEC_last (btrace_insn_s, bfun->insn);
-      pc = last->pc;
 
-      if (gdbarch_insn_is_call (gdbarch, pc))
+      if (last->iclass == BTRACE_INSN_CALL)
        break;
     }
 
   return bfun;
 }
 
-/* Add a continuation segment for a function into which we return.
-   PREV is the chronologically preceding function segment.
+/* Add a continuation segment for a function into which we return at the end of
+   the trace.
+   BTINFO is the branch trace information for the current thread.
    MFUN and FUN are the symbol information we have for this function.  */
 
 static struct btrace_function *
-ftrace_new_return (struct gdbarch *gdbarch,
-                  struct btrace_function *prev,
+ftrace_new_return (struct btrace_thread_info *btinfo,
                   struct minimal_symbol *mfun,
                   struct symbol *fun)
 {
-  struct btrace_function *bfun, *caller;
+  struct btrace_function *prev, *bfun, *caller;
 
-  bfun = ftrace_new_function (prev, mfun, fun);
+  bfun = ftrace_new_function (btinfo, mfun, fun);
+  prev = ftrace_find_call_by_number (btinfo, bfun->number - 1);
 
   /* It is important to start at PREV's caller.  Otherwise, we might find
      PREV itself, if PREV is a recursive function.  */
-  caller = ftrace_find_caller (prev->up, mfun, fun);
+  caller = ftrace_find_call_by_number (btinfo, prev->up);
+  caller = ftrace_find_caller (btinfo, caller, mfun, fun);
   if (caller != NULL)
     {
       /* The caller of PREV is the preceding btrace function segment in this
         function instance.  */
-      gdb_assert (caller->segment.next == NULL);
+      gdb_assert (caller->next == 0);
 
-      caller->segment.next = bfun;
-      bfun->segment.prev = caller;
+      caller->next = bfun->number;
+      bfun->prev = caller->number;
 
       /* Maintain the function level.  */
       bfun->level = caller->level;
@@ -391,33 +448,38 @@ ftrace_new_return (struct gdbarch *gdbarch,
         wrong or that the call is simply not included in the trace.  */
 
       /* Let's search for some actual call.  */
-      caller = ftrace_find_call (gdbarch, prev->up);
+      caller = ftrace_find_call_by_number (btinfo, prev->up);
+      caller = ftrace_find_call (btinfo, caller);
       if (caller == NULL)
        {
          /* There is no call in PREV's back trace.  We assume that the
             branch trace did not include it.  */
 
-         /* Let's find the topmost call function - this skips tail calls.  */
-         while (prev->up != NULL)
-           prev = prev->up;
+         /* Let's find the topmost function and add a new caller for it.
+            This should handle a series of initial tail calls.  */
+         while (prev->up != 0)
+           prev = ftrace_find_call_by_number (btinfo, prev->up);
 
-         /* We maintain levels for a series of returns for which we have
-            not seen the calls.
-            We start at the preceding function's level in case this has
-            already been a return for which we have not seen the call.
-            We start at level 0 otherwise, to handle tail calls correctly.  */
-         bfun->level = min (0, prev->level) - 1;
+         bfun->level = prev->level - 1;
 
          /* Fix up the call stack for PREV.  */
-         ftrace_fixup_caller (prev, bfun, BFUN_UP_LINKS_TO_RET);
+         ftrace_fixup_caller (btinfo, prev, bfun, BFUN_UP_LINKS_TO_RET);
 
          ftrace_debug (bfun, "new return - no caller");
        }
       else
        {
          /* There is a call in PREV's back trace to which we should have
-            returned.  Let's remain at this level.  */
-         bfun->level = prev->level;
+            returned but didn't.  Let's start a new, separate back trace
+            from PREV's level.  */
+         bfun->level = prev->level - 1;
+
+         /* We fix up the back trace for PREV but leave other function segments
+            on the same level as they are.
+            This should handle things like schedule () correctly where we're
+            switching contexts.  */
+         prev->up = bfun->number;
+         prev->flags = BFUN_UP_LINKS_TO_RET;
 
          ftrace_debug (bfun, "new return - unknown caller");
        }
@@ -426,41 +488,70 @@ ftrace_new_return (struct gdbarch *gdbarch,
   return bfun;
 }
 
-/* Add a new function segment for a function switch.
-   PREV is the chronologically preceding function segment.
+/* Add a new function segment for a function switch at the end of the trace.
+   BTINFO is the branch trace information for the current thread.
    MFUN and FUN are the symbol information we have for this function.  */
 
 static struct btrace_function *
-ftrace_new_switch (struct btrace_function *prev,
+ftrace_new_switch (struct btrace_thread_info *btinfo,
                   struct minimal_symbol *mfun,
                   struct symbol *fun)
+{
+  struct btrace_function *prev, *bfun;
+
+  /* This is an unexplained function switch.  We can't really be sure about the
+     call stack, yet the best I can think of right now is to preserve it.  */
+  bfun = ftrace_new_function (btinfo, mfun, fun);
+  prev = ftrace_find_call_by_number (btinfo, bfun->number - 1);
+  bfun->up = prev->up;
+  bfun->flags = prev->flags;
+
+  ftrace_debug (bfun, "new switch");
+
+  return bfun;
+}
+
+/* Add a new function segment for a gap in the trace due to a decode error at
+   the end of the trace.
+   BTINFO is the branch trace information for the current thread.
+   ERRCODE is the format-specific error code.  */
+
+static struct btrace_function *
+ftrace_new_gap (struct btrace_thread_info *btinfo, int errcode,
+               std::vector<unsigned int> &gaps)
 {
   struct btrace_function *bfun;
 
-  /* This is an unexplained function switch.  The call stack will likely
-     be wrong at this point.  */
-  bfun = ftrace_new_function (prev, mfun, fun);
+  if (btinfo->functions.empty ())
+    bfun = ftrace_new_function (btinfo, NULL, NULL);
+  else
+    {
+      /* We hijack the previous function segment if it was empty.  */
+      bfun = &btinfo->functions.back ();
+      if (bfun->errcode != 0 || !VEC_empty (btrace_insn_s, bfun->insn))
+       bfun = ftrace_new_function (btinfo, NULL, NULL);
+    }
 
-  /* We keep the function level.  */
-  bfun->level = prev->level;
+  bfun->errcode = errcode;
+  gaps.push_back (bfun->number);
 
-  ftrace_debug (bfun, "new switch");
+  ftrace_debug (bfun, "new gap");
 
   return bfun;
 }
 
-/* Update BFUN with respect to the instruction at PC.  This may create new
-   function segments.
+/* Update the current function segment at the end of the trace in BTINFO with
+   respect to the instruction at PC.  This may create new function segments.
    Return the chronologically latest function segment, never NULL.  */
 
 static struct btrace_function *
-ftrace_update_function (struct gdbarch *gdbarch,
-                       struct btrace_function *bfun, CORE_ADDR pc)
+ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
 {
   struct bound_minimal_symbol bmfun;
   struct minimal_symbol *mfun;
   struct symbol *fun;
   struct btrace_insn *last;
+  struct btrace_function *bfun;
 
   /* Try to determine the function we're in.  We use both types of symbols
      to avoid surprises when we sometimes get a full symbol and sometimes
@@ -472,9 +563,14 @@ ftrace_update_function (struct gdbarch *gdbarch,
   if (fun == NULL && mfun == NULL)
     DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
 
-  /* If we didn't have a function before, we create one.  */
-  if (bfun == NULL)
-    return ftrace_new_function (bfun, mfun, fun);
+  /* If we didn't have a function, we create one.  */
+  if (btinfo->functions.empty ())
+    return ftrace_new_function (btinfo, mfun, fun);
+
+  /* If we had a gap before, we create a function.  */
+  bfun = &btinfo->functions.back ();
+  if (bfun->errcode != 0)
+    return ftrace_new_function (btinfo, mfun, fun);
 
   /* Check the last instruction, if we have one.
      We do this check first, since it allows us to fill in the call stack
@@ -485,24 +581,53 @@ ftrace_update_function (struct gdbarch *gdbarch,
 
   if (last != NULL)
     {
-      CORE_ADDR lpc;
+      switch (last->iclass)
+       {
+       case BTRACE_INSN_RETURN:
+         {
+           const char *fname;
+
+           /* On some systems, _dl_runtime_resolve returns to the resolved
+              function instead of jumping to it.  From our perspective,
+              however, this is a tailcall.
+              If we treated it as return, we wouldn't be able to find the
+              resolved function in our stack back trace.  Hence, we would
+              lose the current stack back trace and start anew with an empty
+              back trace.  When the resolved function returns, we would then
+              create a stack back trace with the same function names but
+              different frame id's.  This will confuse stepping.  */
+           fname = ftrace_print_function_name (bfun);
+           if (strcmp (fname, "_dl_runtime_resolve") == 0)
+             return ftrace_new_tailcall (btinfo, mfun, fun);
+
+           return ftrace_new_return (btinfo, mfun, fun);
+         }
+
+       case BTRACE_INSN_CALL:
+         /* Ignore calls to the next instruction.  They are used for PIC.  */
+         if (last->pc + last->size == pc)
+           break;
 
-      lpc = last->pc;
+         return ftrace_new_call (btinfo, mfun, fun);
 
-      /* Check for returns.  */
-      if (gdbarch_insn_is_ret (gdbarch, lpc))
-       return ftrace_new_return (gdbarch, bfun, mfun, fun);
+       case BTRACE_INSN_JUMP:
+         {
+           CORE_ADDR start;
 
-      /* Check for calls.  */
-      if (gdbarch_insn_is_call (gdbarch, lpc))
-       {
-         int size;
+           start = get_pc_function_start (pc);
 
-         size = gdb_insn_length (gdbarch, lpc);
+           /* A jump to the start of a function is (typically) a tail call.  */
+           if (start == pc)
+             return ftrace_new_tailcall (btinfo, mfun, fun);
 
-         /* Ignore calls to the next instruction.  They are used for PIC.  */
-         if (lpc + size != pc)
-           return ftrace_new_call (bfun, mfun, fun);
+           /* If we can't determine the function for PC, we treat a jump at
+              the end of the block as tail call if we're switching functions
+              and as an intra-function branch if we don't.  */
+           if (start == 0 && ftrace_function_switched (bfun, mfun, fun))
+             return ftrace_new_tailcall (btinfo, mfun, fun);
+
+           break;
+         }
        }
     }
 
@@ -514,94 +639,420 @@ ftrace_update_function (struct gdbarch *gdbarch,
                    ftrace_print_function_name (bfun),
                    ftrace_print_filename (bfun));
 
-      if (last != NULL)
-       {
-         CORE_ADDR start, lpc;
+      return ftrace_new_switch (btinfo, mfun, fun);
+    }
+
+  return bfun;
+}
+
+/* Add the instruction at PC to BFUN's instructions.  */
 
-         start = get_pc_function_start (pc);
+static void
+ftrace_update_insns (struct btrace_function *bfun,
+                    const struct btrace_insn *insn)
+{
+  VEC_safe_push (btrace_insn_s, bfun->insn, insn);
 
-         /* If we can't determine the function for PC, we treat a jump at
-            the end of the block as tail call.  */
-         if (start == 0)
-           start = pc;
+  if (record_debug > 1)
+    ftrace_debug (bfun, "update insn");
+}
 
-         lpc = last->pc;
+/* Classify the instruction at PC.  */
 
-         /* Jumps indicate optimized tail calls.  */
-         if (start == pc && gdbarch_insn_is_jump (gdbarch, lpc))
-           return ftrace_new_tailcall (bfun, mfun, fun);
-       }
+static enum btrace_insn_class
+ftrace_classify_insn (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  enum btrace_insn_class iclass;
 
-      return ftrace_new_switch (bfun, mfun, fun);
+  iclass = BTRACE_INSN_OTHER;
+  TRY
+    {
+      if (gdbarch_insn_is_call (gdbarch, pc))
+       iclass = BTRACE_INSN_CALL;
+      else if (gdbarch_insn_is_ret (gdbarch, pc))
+       iclass = BTRACE_INSN_RETURN;
+      else if (gdbarch_insn_is_jump (gdbarch, pc))
+       iclass = BTRACE_INSN_JUMP;
+    }
+  CATCH (error, RETURN_MASK_ERROR)
+    {
     }
+  END_CATCH
 
-  return bfun;
+  return iclass;
+}
+
+/* Try to match the back trace at LHS to the back trace at RHS.  Returns the
+   number of matching function segments or zero if the back traces do not
+   match.  BTINFO is the branch trace information for the current thread.  */
+
+static int
+ftrace_match_backtrace (struct btrace_thread_info *btinfo,
+                       struct btrace_function *lhs,
+                       struct btrace_function *rhs)
+{
+  int matches;
+
+  for (matches = 0; lhs != NULL && rhs != NULL; ++matches)
+    {
+      if (ftrace_function_switched (lhs, rhs->msym, rhs->sym))
+       return 0;
+
+      lhs = ftrace_get_caller (btinfo, lhs);
+      rhs = ftrace_get_caller (btinfo, rhs);
+    }
+
+  return matches;
 }
 
-/* Update BFUN's source range with respect to the instruction at PC.  */
+/* Add ADJUSTMENT to the level of BFUN and succeeding function segments.
+   BTINFO is the branch trace information for the current thread.  */
 
 static void
-ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc)
+ftrace_fixup_level (struct btrace_thread_info *btinfo,
+                   struct btrace_function *bfun, int adjustment)
 {
-  struct symtab_and_line sal;
-  const char *fullname;
+  if (adjustment == 0)
+    return;
 
-  sal = find_pc_line (pc, 0);
-  if (sal.symtab == NULL || sal.line == 0)
+  DEBUG_FTRACE ("fixup level (%+d)", adjustment);
+  ftrace_debug (bfun, "..bfun");
+
+  while (bfun != NULL)
     {
-      DEBUG_FTRACE ("no lines at %s", core_addr_to_string_nz (pc));
-      return;
+      bfun->level += adjustment;
+      bfun = ftrace_find_call_by_number (btinfo, bfun->number + 1);
+    }
+}
+
+/* Recompute the global level offset.  Traverse the function trace and compute
+   the global level offset as the negative of the minimal function level.  */
+
+static void
+ftrace_compute_global_level_offset (struct btrace_thread_info *btinfo)
+{
+  int level = INT_MAX;
+
+  if (btinfo == NULL)
+    return;
+
+  if (btinfo->functions.empty ())
+    return;
+
+  unsigned int length = btinfo->functions.size() - 1;
+  for (unsigned int i = 0; i < length; ++i)
+    level = std::min (level, btinfo->functions[i].level);
+
+  /* The last function segment contains the current instruction, which is not
+     really part of the trace.  If it contains just this one instruction, we
+     ignore the segment.  */
+  struct btrace_function *last = &btinfo->functions.back();
+  if (VEC_length (btrace_insn_s, last->insn) != 1)
+    level = std::min (level, last->level);
+
+  DEBUG_FTRACE ("setting global level offset: %d", -level);
+  btinfo->level = -level;
+}
+
+/* Connect the function segments PREV and NEXT in a bottom-to-top walk as in
+   ftrace_connect_backtrace.  BTINFO is the branch trace information for the
+   current thread.  */
+
+static void
+ftrace_connect_bfun (struct btrace_thread_info *btinfo,
+                    struct btrace_function *prev,
+                    struct btrace_function *next)
+{
+  DEBUG_FTRACE ("connecting...");
+  ftrace_debug (prev, "..prev");
+  ftrace_debug (next, "..next");
+
+  /* The function segments are not yet connected.  */
+  gdb_assert (prev->next == 0);
+  gdb_assert (next->prev == 0);
+
+  prev->next = next->number;
+  next->prev = prev->number;
+
+  /* We may have moved NEXT to a different function level.  */
+  ftrace_fixup_level (btinfo, next, prev->level - next->level);
+
+  /* If we run out of back trace for one, let's use the other's.  */
+  if (prev->up == 0)
+    {
+      const btrace_function_flags flags = next->flags;
+
+      next = ftrace_find_call_by_number (btinfo, next->up);
+      if (next != NULL)
+       {
+         DEBUG_FTRACE ("using next's callers");
+         ftrace_fixup_caller (btinfo, prev, next, flags);
+       }
     }
+  else if (next->up == 0)
+    {
+      const btrace_function_flags flags = prev->flags;
 
-  /* Check if we switched files.  This could happen if, say, a macro that
-     is defined in another file is expanded here.  */
-  fullname = symtab_to_fullname (sal.symtab);
-  if (ftrace_skip_file (bfun, fullname))
+      prev = ftrace_find_call_by_number (btinfo, prev->up);
+      if (prev != NULL)
+       {
+         DEBUG_FTRACE ("using prev's callers");
+         ftrace_fixup_caller (btinfo, next, prev, flags);
+       }
+    }
+  else
     {
-      DEBUG_FTRACE ("ignoring file at %s, file=%s",
-                   core_addr_to_string_nz (pc), fullname);
-      return;
+      /* PREV may have a tailcall caller, NEXT can't.  If it does, fixup the up
+        link to add the tail callers to NEXT's back trace.
+
+        This removes NEXT->UP from NEXT's back trace.  It will be added back
+        when connecting NEXT and PREV's callers - provided they exist.
+
+        If PREV's back trace consists of a series of tail calls without an
+        actual call, there will be no further connection and NEXT's caller will
+        be removed for good.  To catch this case, we handle it here and connect
+        the top of PREV's back trace to NEXT's caller.  */
+      if ((prev->flags & BFUN_UP_LINKS_TO_TAILCALL) != 0)
+       {
+         struct btrace_function *caller;
+         btrace_function_flags next_flags, prev_flags;
+
+         /* We checked NEXT->UP above so CALLER can't be NULL.  */
+         caller = ftrace_find_call_by_number (btinfo, next->up);
+         next_flags = next->flags;
+         prev_flags = prev->flags;
+
+         DEBUG_FTRACE ("adding prev's tail calls to next");
+
+         prev = ftrace_find_call_by_number (btinfo, prev->up);
+         ftrace_fixup_caller (btinfo, next, prev, prev_flags);
+
+         for (; prev != NULL; prev = ftrace_find_call_by_number (btinfo,
+                                                                 prev->up))
+           {
+             /* At the end of PREV's back trace, continue with CALLER.  */
+             if (prev->up == 0)
+               {
+                 DEBUG_FTRACE ("fixing up link for tailcall chain");
+                 ftrace_debug (prev, "..top");
+                 ftrace_debug (caller, "..up");
+
+                 ftrace_fixup_caller (btinfo, prev, caller, next_flags);
+
+                 /* If we skipped any tail calls, this may move CALLER to a
+                    different function level.
+
+                    Note that changing CALLER's level is only OK because we
+                    know that this is the last iteration of the bottom-to-top
+                    walk in ftrace_connect_backtrace.
+
+                    Otherwise we will fix up CALLER's level when we connect it
+                    to PREV's caller in the next iteration.  */
+                 ftrace_fixup_level (btinfo, caller,
+                                     prev->level - caller->level - 1);
+                 break;
+               }
+
+             /* There's nothing to do if we find a real call.  */
+             if ((prev->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
+               {
+                 DEBUG_FTRACE ("will fix up link in next iteration");
+                 break;
+               }
+           }
+       }
     }
+}
 
-  /* Update the line range.  */
-  bfun->lbegin = min (bfun->lbegin, sal.line);
-  bfun->lend = max (bfun->lend, sal.line);
+/* Connect function segments on the same level in the back trace at LHS and RHS.
+   The back traces at LHS and RHS are expected to match according to
+   ftrace_match_backtrace.  BTINFO is the branch trace information for the
+   current thread.  */
 
-  if (record_debug > 1)
-    ftrace_debug (bfun, "update lines");
+static void
+ftrace_connect_backtrace (struct btrace_thread_info *btinfo,
+                         struct btrace_function *lhs,
+                         struct btrace_function *rhs)
+{
+  while (lhs != NULL && rhs != NULL)
+    {
+      struct btrace_function *prev, *next;
+
+      gdb_assert (!ftrace_function_switched (lhs, rhs->msym, rhs->sym));
+
+      /* Connecting LHS and RHS may change the up link.  */
+      prev = lhs;
+      next = rhs;
+
+      lhs = ftrace_get_caller (btinfo, lhs);
+      rhs = ftrace_get_caller (btinfo, rhs);
+
+      ftrace_connect_bfun (btinfo, prev, next);
+    }
 }
 
-/* Add the instruction at PC to BFUN's instructions.  */
+/* Bridge the gap between two function segments left and right of a gap if their
+   respective back traces match in at least MIN_MATCHES functions.  BTINFO is
+   the branch trace information for the current thread.
+
+   Returns non-zero if the gap could be bridged, zero otherwise.  */
+
+static int
+ftrace_bridge_gap (struct btrace_thread_info *btinfo,
+                  struct btrace_function *lhs, struct btrace_function *rhs,
+                  int min_matches)
+{
+  struct btrace_function *best_l, *best_r, *cand_l, *cand_r;
+  int best_matches;
+
+  DEBUG_FTRACE ("checking gap at insn %u (req matches: %d)",
+               rhs->insn_offset - 1, min_matches);
+
+  best_matches = 0;
+  best_l = NULL;
+  best_r = NULL;
+
+  /* We search the back traces of LHS and RHS for valid connections and connect
+     the two functon segments that give the longest combined back trace.  */
+
+  for (cand_l = lhs; cand_l != NULL;
+       cand_l = ftrace_get_caller (btinfo, cand_l))
+    for (cand_r = rhs; cand_r != NULL;
+        cand_r = ftrace_get_caller (btinfo, cand_r))
+      {
+       int matches;
+
+       matches = ftrace_match_backtrace (btinfo, cand_l, cand_r);
+       if (best_matches < matches)
+         {
+           best_matches = matches;
+           best_l = cand_l;
+           best_r = cand_r;
+         }
+      }
+
+  /* We need at least MIN_MATCHES matches.  */
+  gdb_assert (min_matches > 0);
+  if (best_matches < min_matches)
+    return 0;
+
+  DEBUG_FTRACE ("..matches: %d", best_matches);
+
+  /* We will fix up the level of BEST_R and succeeding function segments such
+     that BEST_R's level matches BEST_L's when we connect BEST_L to BEST_R.
+
+     This will ignore the level of RHS and following if BEST_R != RHS.  I.e. if
+     BEST_R is a successor of RHS in the back trace of RHS (phases 1 and 3).
+
+     To catch this, we already fix up the level here where we can start at RHS
+     instead of at BEST_R.  We will ignore the level fixup when connecting
+     BEST_L to BEST_R as they will already be on the same level.  */
+  ftrace_fixup_level (btinfo, rhs, best_l->level - best_r->level);
+
+  ftrace_connect_backtrace (btinfo, best_l, best_r);
+
+  return best_matches;
+}
+
+/* Try to bridge gaps due to overflow or decode errors by connecting the
+   function segments that are separated by the gap.  */
 
 static void
-ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
+btrace_bridge_gaps (struct thread_info *tp, std::vector<unsigned int> &gaps)
 {
-  struct btrace_insn *insn;
+  struct btrace_thread_info *btinfo = &tp->btrace;
+  std::vector<unsigned int> remaining;
+  int min_matches;
 
-  insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL);
-  insn->pc = pc;
+  DEBUG ("bridge gaps");
 
-  if (record_debug > 1)
-    ftrace_debug (bfun, "update insn");
+  /* We require a minimum amount of matches for bridging a gap.  The number of
+     required matches will be lowered with each iteration.
+
+     The more matches the higher our confidence that the bridging is correct.
+     For big gaps or small traces, however, it may not be feasible to require a
+     high number of matches.  */
+  for (min_matches = 5; min_matches > 0; --min_matches)
+    {
+      /* Let's try to bridge as many gaps as we can.  In some cases, we need to
+        skip a gap and revisit it again after we closed later gaps.  */
+      while (!gaps.empty ())
+       {
+         for (const unsigned int number : gaps)
+           {
+             struct btrace_function *gap, *lhs, *rhs;
+             int bridged;
+
+             gap = ftrace_find_call_by_number (btinfo, number);
+
+             /* We may have a sequence of gaps if we run from one error into
+                the next as we try to re-sync onto the trace stream.  Ignore
+                all but the leftmost gap in such a sequence.
+
+                Also ignore gaps at the beginning of the trace.  */
+             lhs = ftrace_find_call_by_number (btinfo, gap->number - 1);
+             if (lhs == NULL || lhs->errcode != 0)
+               continue;
+
+             /* Skip gaps to the right.  */
+             rhs = ftrace_find_call_by_number (btinfo, gap->number + 1);
+             while (rhs != NULL && rhs->errcode != 0)
+               rhs = ftrace_find_call_by_number (btinfo, rhs->number + 1);
+
+             /* Ignore gaps at the end of the trace.  */
+             if (rhs == NULL)
+               continue;
+
+             bridged = ftrace_bridge_gap (btinfo, lhs, rhs, min_matches);
+
+             /* Keep track of gaps we were not able to bridge and try again.
+                If we just pushed them to the end of GAPS we would risk an
+                infinite loop in case we simply cannot bridge a gap.  */
+             if (bridged == 0)
+               remaining.push_back (number);
+           }
+
+         /* Let's see if we made any progress.  */
+         if (remaining.size () == gaps.size ())
+           break;
+
+         gaps.clear ();
+         gaps.swap (remaining);
+       }
+
+      /* We get here if either GAPS is empty or if GAPS equals REMAINING.  */
+      if (gaps.empty ())
+       break;
+
+      remaining.clear ();
+    }
+
+  /* We may omit this in some cases.  Not sure it is worth the extra
+     complication, though.  */
+  ftrace_compute_global_level_offset (btinfo);
 }
 
 /* Compute the function branch trace from BTS trace.  */
 
 static void
-btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
-                          const struct btrace_data_bts *btrace)
+btrace_compute_ftrace_bts (struct thread_info *tp,
+                          const struct btrace_data_bts *btrace,
+                          std::vector<unsigned int> &gaps)
 {
-  struct btrace_function *begin, *end;
+  struct btrace_thread_info *btinfo;
   struct gdbarch *gdbarch;
   unsigned int blk;
   int level;
 
   gdbarch = target_gdbarch ();
-  begin = btinfo->begin;
-  end = btinfo->end;
-  level = begin != NULL ? -btinfo->level : INT_MAX;
+  btinfo = &tp->btrace;
   blk = VEC_length (btrace_block_s, btrace->blocks);
 
+  if (btinfo->functions.empty ())
+    level = INT_MAX;
+  else
+    level = -btinfo->level;
+
   while (blk != 0)
     {
       btrace_block_s *block;
@@ -614,39 +1065,62 @@ btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
 
       for (;;)
        {
+         struct btrace_function *bfun;
+         struct btrace_insn insn;
          int size;
 
          /* We should hit the end of the block.  Warn if we went too far.  */
          if (block->end < pc)
            {
-             warning (_("Recorded trace may be corrupted around %s."),
+             /* Indicate the gap in the trace.  */
+             bfun = ftrace_new_gap (btinfo, BDE_BTS_OVERFLOW, gaps);
+
+             warning (_("Recorded trace may be corrupted at instruction "
+                        "%u (pc = %s)."), bfun->insn_offset - 1,
                       core_addr_to_string_nz (pc));
+
              break;
            }
 
-         end = ftrace_update_function (gdbarch, end, pc);
-         if (begin == NULL)
-           begin = end;
+         bfun = ftrace_update_function (btinfo, pc);
 
          /* Maintain the function level offset.
             For all but the last block, we do it here.  */
          if (blk != 0)
-           level = min (level, end->level);
+           level = std::min (level, bfun->level);
+
+         size = 0;
+         TRY
+           {
+             size = gdb_insn_length (gdbarch, pc);
+           }
+         CATCH (error, RETURN_MASK_ERROR)
+           {
+           }
+         END_CATCH
 
-         ftrace_update_insns (end, pc);
-         ftrace_update_lines (end, pc);
+         insn.pc = pc;
+         insn.size = size;
+         insn.iclass = ftrace_classify_insn (gdbarch, pc);
+         insn.flags = 0;
+
+         ftrace_update_insns (bfun, &insn);
 
          /* We're done once we pushed the instruction at the end.  */
          if (block->end == pc)
            break;
 
-         size = gdb_insn_length (gdbarch, pc);
-
-         /* Make sure we terminate if we fail to compute the size.  */
+         /* We can't continue if we fail to compute the size.  */
          if (size <= 0)
            {
-             warning (_("Recorded trace may be incomplete around %s."),
+             /* Indicate the gap in the trace.  We just added INSN so we're
+                not at the beginning.  */
+             bfun = ftrace_new_gap (btinfo, BDE_BTS_INSN_SIZE, gaps);
+
+             warning (_("Recorded trace may be incomplete at instruction %u "
+                        "(pc = %s)."), bfun->insn_offset - 1,
                       core_addr_to_string_nz (pc));
+
              break;
            }
 
@@ -659,57 +1133,434 @@ btrace_compute_ftrace_bts (struct btrace_thread_info *btinfo,
             and is not really part of the execution history, it shouldn't
             affect the level.  */
          if (blk == 0)
-           level = min (level, end->level);
+           level = std::min (level, bfun->level);
        }
     }
 
-  btinfo->begin = begin;
-  btinfo->end = end;
-
   /* LEVEL is the minimal function level of all btrace function segments.
      Define the global level offset to -LEVEL so all function levels are
      normalized to start at zero.  */
   btinfo->level = -level;
 }
 
-/* Compute the function branch trace from a block branch trace BTRACE for
-   a thread given by BTINFO.  */
+#if defined (HAVE_LIBIPT)
 
-static void
-btrace_compute_ftrace (struct btrace_thread_info *btinfo,
-                      struct btrace_data *btrace)
+static enum btrace_insn_class
+pt_reclassify_insn (enum pt_insn_class iclass)
 {
-  DEBUG ("compute ftrace");
-
-  switch (btrace->format)
+  switch (iclass)
     {
-    case BTRACE_FORMAT_NONE:
-      return;
+    case ptic_call:
+      return BTRACE_INSN_CALL;
 
-    case BTRACE_FORMAT_BTS:
-      btrace_compute_ftrace_bts (btinfo, &btrace->variant.bts);
-      return;
-    }
+    case ptic_return:
+      return BTRACE_INSN_RETURN;
 
-  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+    case ptic_jump:
+      return BTRACE_INSN_JUMP;
+
+    default:
+      return BTRACE_INSN_OTHER;
+    }
 }
 
-/* Add an entry for the current PC.  */
+/* Return the btrace instruction flags for INSN.  */
 
-static void
-btrace_add_pc (struct thread_info *tp)
+static btrace_insn_flags
+pt_btrace_insn_flags (const struct pt_insn &insn)
 {
-  struct btrace_data btrace;
-  struct btrace_block *block;
-  struct regcache *regcache;
-  struct cleanup *cleanup;
-  CORE_ADDR pc;
+  btrace_insn_flags flags = 0;
 
-  regcache = get_thread_regcache (tp->ptid);
-  pc = regcache_read_pc (regcache);
+  if (insn.speculative)
+    flags |= BTRACE_INSN_FLAG_SPECULATIVE;
 
-  btrace_data_init (&btrace);
-  btrace.format = BTRACE_FORMAT_BTS;
+  return flags;
+}
+
+/* Return the btrace instruction for INSN.  */
+
+static btrace_insn
+pt_btrace_insn (const struct pt_insn &insn)
+{
+  return {(CORE_ADDR) insn.ip, (gdb_byte) insn.size,
+         pt_reclassify_insn (insn.iclass),
+         pt_btrace_insn_flags (insn)};
+}
+
+/* Handle instruction decode events (libipt-v2).  */
+
+static int
+handle_pt_insn_events (struct btrace_thread_info *btinfo,
+                      struct pt_insn_decoder *decoder,
+                      std::vector<unsigned int> &gaps, int status)
+{
+#if defined (HAVE_PT_INSN_EVENT)
+  while (status & pts_event_pending)
+    {
+      struct btrace_function *bfun;
+      struct pt_event event;
+      uint64_t offset;
+
+      status = pt_insn_event (decoder, &event, sizeof (event));
+      if (status < 0)
+       break;
+
+      switch (event.type)
+       {
+       default:
+         break;
+
+       case ptev_enabled:
+         if (event.variant.enabled.resumed == 0 && !btinfo->functions.empty ())
+           {
+             bfun = ftrace_new_gap (btinfo, BDE_PT_DISABLED, gaps);
+
+             pt_insn_get_offset (decoder, &offset);
+
+             warning (_("Non-contiguous trace at instruction %u (offset = 0x%"
+                        PRIx64 ")."), bfun->insn_offset - 1, offset);
+           }
+
+         break;
+
+       case ptev_overflow:
+         bfun = ftrace_new_gap (btinfo, BDE_PT_OVERFLOW, gaps);
+
+         pt_insn_get_offset (decoder, &offset);
+
+         warning (_("Overflow at instruction %u (offset = 0x%" PRIx64 ")."),
+                  bfun->insn_offset - 1, offset);
+
+         break;
+       }
+    }
+#endif /* defined (HAVE_PT_INSN_EVENT) */
+
+  return status;
+}
+
+/* Handle events indicated by flags in INSN (libipt-v1).  */
+
+static void
+handle_pt_insn_event_flags (struct btrace_thread_info *btinfo,
+                           struct pt_insn_decoder *decoder,
+                           const struct pt_insn &insn,
+                           std::vector<unsigned int> &gaps)
+{
+#if defined (HAVE_STRUCT_PT_INSN_ENABLED)
+  /* Tracing is disabled and re-enabled each time we enter the kernel.  Most
+     times, we continue from the same instruction we stopped before.  This is
+     indicated via the RESUMED instruction flag.  The ENABLED instruction flag
+     means that we continued from some other instruction.  Indicate this as a
+     trace gap except when tracing just started.  */
+  if (insn.enabled && !btinfo->functions.empty ())
+    {
+      struct btrace_function *bfun;
+      uint64_t offset;
+
+      bfun = ftrace_new_gap (btinfo, BDE_PT_DISABLED, gaps);
+
+      pt_insn_get_offset (decoder, &offset);
+
+      warning (_("Non-contiguous trace at instruction %u (offset = 0x%" PRIx64
+                ", pc = 0x%" PRIx64 ")."), bfun->insn_offset - 1, offset,
+              insn.ip);
+    }
+#endif /* defined (HAVE_STRUCT_PT_INSN_ENABLED) */
+
+#if defined (HAVE_STRUCT_PT_INSN_RESYNCED)
+  /* Indicate trace overflows.  */
+  if (insn.resynced)
+    {
+      struct btrace_function *bfun;
+      uint64_t offset;
+
+      bfun = ftrace_new_gap (btinfo, BDE_PT_OVERFLOW, gaps);
+
+      pt_insn_get_offset (decoder, &offset);
+
+      warning (_("Overflow at instruction %u (offset = 0x%" PRIx64 ", pc = 0x%"
+                PRIx64 ")."), bfun->insn_offset - 1, offset, insn.ip);
+    }
+#endif /* defined (HAVE_STRUCT_PT_INSN_RESYNCED) */
+}
+
+/* Add function branch trace to BTINFO using DECODER.  */
+
+static void
+ftrace_add_pt (struct btrace_thread_info *btinfo,
+              struct pt_insn_decoder *decoder,
+              int *plevel,
+              std::vector<unsigned int> &gaps)
+{
+  struct btrace_function *bfun;
+  uint64_t offset;
+  int status;
+
+  for (;;)
+    {
+      struct pt_insn insn;
+
+      status = pt_insn_sync_forward (decoder);
+      if (status < 0)
+       {
+         if (status != -pte_eos)
+           warning (_("Failed to synchronize onto the Intel Processor "
+                      "Trace stream: %s."), pt_errstr (pt_errcode (status)));
+         break;
+       }
+
+      for (;;)
+       {
+         /* Handle events from the previous iteration or synchronization.  */
+         status = handle_pt_insn_events (btinfo, decoder, gaps, status);
+         if (status < 0)
+           break;
+
+         status = pt_insn_next (decoder, &insn, sizeof(insn));
+         if (status < 0)
+           break;
+
+         /* Handle events indicated by flags in INSN.  */
+         handle_pt_insn_event_flags (btinfo, decoder, insn, gaps);
+
+         bfun = ftrace_update_function (btinfo, insn.ip);
+
+         /* Maintain the function level offset.  */
+         *plevel = std::min (*plevel, bfun->level);
+
+         btrace_insn btinsn = pt_btrace_insn (insn);
+         ftrace_update_insns (bfun, &btinsn);
+       }
+
+      if (status == -pte_eos)
+       break;
+
+      /* Indicate the gap in the trace.  */
+      bfun = ftrace_new_gap (btinfo, status, gaps);
+
+      pt_insn_get_offset (decoder, &offset);
+
+      warning (_("Decode error (%d) at instruction %u (offset = 0x%" PRIx64
+                ", pc = 0x%" PRIx64 "): %s."), status, bfun->insn_offset - 1,
+              offset, insn.ip, pt_errstr (pt_errcode (status)));
+    }
+}
+
+/* A callback function to allow the trace decoder to read the inferior's
+   memory.  */
+
+static int
+btrace_pt_readmem_callback (gdb_byte *buffer, size_t size,
+                           const struct pt_asid *asid, uint64_t pc,
+                           void *context)
+{
+  int result, errcode;
+
+  result = (int) size;
+  TRY
+    {
+      errcode = target_read_code ((CORE_ADDR) pc, buffer, size);
+      if (errcode != 0)
+       result = -pte_nomap;
+    }
+  CATCH (error, RETURN_MASK_ERROR)
+    {
+      result = -pte_nomap;
+    }
+  END_CATCH
+
+  return result;
+}
+
+/* Translate the vendor from one enum to another.  */
+
+static enum pt_cpu_vendor
+pt_translate_cpu_vendor (enum btrace_cpu_vendor vendor)
+{
+  switch (vendor)
+    {
+    default:
+      return pcv_unknown;
+
+    case CV_INTEL:
+      return pcv_intel;
+    }
+}
+
+/* Finalize the function branch trace after decode.  */
+
+static void btrace_finalize_ftrace_pt (struct pt_insn_decoder *decoder,
+                                      struct thread_info *tp, int level)
+{
+  pt_insn_free_decoder (decoder);
+
+  /* LEVEL is the minimal function level of all btrace function segments.
+     Define the global level offset to -LEVEL so all function levels are
+     normalized to start at zero.  */
+  tp->btrace.level = -level;
+
+  /* Add a single last instruction entry for the current PC.
+     This allows us to compute the backtrace at the current PC using both
+     standard unwind and btrace unwind.
+     This extra entry is ignored by all record commands.  */
+  btrace_add_pc (tp);
+}
+
+/* Compute the function branch trace from Intel Processor Trace
+   format.  */
+
+static void
+btrace_compute_ftrace_pt (struct thread_info *tp,
+                         const struct btrace_data_pt *btrace,
+                         std::vector<unsigned int> &gaps)
+{
+  struct btrace_thread_info *btinfo;
+  struct pt_insn_decoder *decoder;
+  struct pt_config config;
+  int level, errcode;
+
+  if (btrace->size == 0)
+    return;
+
+  btinfo = &tp->btrace;
+  if (btinfo->functions.empty ())
+    level = INT_MAX;
+  else
+    level = -btinfo->level;
+
+  pt_config_init(&config);
+  config.begin = btrace->data;
+  config.end = btrace->data + btrace->size;
+
+  config.cpu.vendor = pt_translate_cpu_vendor (btrace->config.cpu.vendor);
+  config.cpu.family = btrace->config.cpu.family;
+  config.cpu.model = btrace->config.cpu.model;
+  config.cpu.stepping = btrace->config.cpu.stepping;
+
+  errcode = pt_cpu_errata (&config.errata, &config.cpu);
+  if (errcode < 0)
+    error (_("Failed to configure the Intel Processor Trace decoder: %s."),
+          pt_errstr (pt_errcode (errcode)));
+
+  decoder = pt_insn_alloc_decoder (&config);
+  if (decoder == NULL)
+    error (_("Failed to allocate the Intel Processor Trace decoder."));
+
+  TRY
+    {
+      struct pt_image *image;
+
+      image = pt_insn_get_image(decoder);
+      if (image == NULL)
+       error (_("Failed to configure the Intel Processor Trace decoder."));
+
+      errcode = pt_image_set_callback(image, btrace_pt_readmem_callback, NULL);
+      if (errcode < 0)
+       error (_("Failed to configure the Intel Processor Trace decoder: "
+                "%s."), pt_errstr (pt_errcode (errcode)));
+
+      ftrace_add_pt (btinfo, decoder, &level, gaps);
+    }
+  CATCH (error, RETURN_MASK_ALL)
+    {
+      /* Indicate a gap in the trace if we quit trace processing.  */
+      if (error.reason == RETURN_QUIT && !btinfo->functions.empty ())
+       ftrace_new_gap (btinfo, BDE_PT_USER_QUIT, gaps);
+
+      btrace_finalize_ftrace_pt (decoder, tp, level);
+
+      throw_exception (error);
+    }
+  END_CATCH
+
+  btrace_finalize_ftrace_pt (decoder, tp, level);
+}
+
+#else /* defined (HAVE_LIBIPT)  */
+
+static void
+btrace_compute_ftrace_pt (struct thread_info *tp,
+                         const struct btrace_data_pt *btrace,
+                         std::vector<unsigned int> &gaps)
+{
+  internal_error (__FILE__, __LINE__, _("Unexpected branch trace format."));
+}
+
+#endif /* defined (HAVE_LIBIPT)  */
+
+/* Compute the function branch trace from a block branch trace BTRACE for
+   a thread given by BTINFO.  */
+
+static void
+btrace_compute_ftrace_1 (struct thread_info *tp, struct btrace_data *btrace,
+                        std::vector<unsigned int> &gaps)
+{
+  DEBUG ("compute ftrace");
+
+  switch (btrace->format)
+    {
+    case BTRACE_FORMAT_NONE:
+      return;
+
+    case BTRACE_FORMAT_BTS:
+      btrace_compute_ftrace_bts (tp, &btrace->variant.bts, gaps);
+      return;
+
+    case BTRACE_FORMAT_PT:
+      btrace_compute_ftrace_pt (tp, &btrace->variant.pt, gaps);
+      return;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
+}
+
+static void
+btrace_finalize_ftrace (struct thread_info *tp, std::vector<unsigned int> &gaps)
+{
+  if (!gaps.empty ())
+    {
+      tp->btrace.ngaps += gaps.size ();
+      btrace_bridge_gaps (tp, gaps);
+    }
+}
+
+static void
+btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace)
+{
+  std::vector<unsigned int> gaps;
+
+  TRY
+    {
+      btrace_compute_ftrace_1 (tp, btrace, gaps);
+    }
+  CATCH (error, RETURN_MASK_ALL)
+    {
+      btrace_finalize_ftrace (tp, gaps);
+
+      throw_exception (error);
+    }
+  END_CATCH
+
+  btrace_finalize_ftrace (tp, gaps);
+}
+
+/* Add an entry for the current PC.  */
+
+static void
+btrace_add_pc (struct thread_info *tp)
+{
+  struct btrace_data btrace;
+  struct btrace_block *block;
+  struct regcache *regcache;
+  struct cleanup *cleanup;
+  CORE_ADDR pc;
+
+  regcache = get_thread_regcache (tp->ptid);
+  pc = regcache_read_pc (regcache);
+
+  btrace_data_init (&btrace);
+  btrace.format = BTRACE_FORMAT_BTS;
   btrace.variant.bts.blocks = NULL;
 
   cleanup = make_cleanup_btrace_data (&btrace);
@@ -718,7 +1569,7 @@ btrace_add_pc (struct thread_info *tp)
   block->begin = pc;
   block->end = pc;
 
-  btrace_compute_ftrace (&tp->btrace, &btrace);
+  btrace_compute_ftrace (tp, &btrace);
 
   do_cleanups (cleanup);
 }
@@ -731,17 +1582,46 @@ btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
   if (tp->btrace.target != NULL)
     return;
 
+#if !defined (HAVE_LIBIPT)
+  if (conf->format == BTRACE_FORMAT_PT)
+    error (_("GDB does not support Intel Processor Trace."));
+#endif /* !defined (HAVE_LIBIPT) */
+
   if (!target_supports_btrace (conf->format))
     error (_("Target does not support branch tracing."));
 
-  DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("enable thread %s (%s)", print_thread_id (tp),
+        target_pid_to_str (tp->ptid));
 
   tp->btrace.target = target_enable_btrace (tp->ptid, conf);
 
-  /* Add an entry for the current PC so we start tracing from where we
-     enabled it.  */
-  if (tp->btrace.target != NULL)
-    btrace_add_pc (tp);
+  /* We're done if we failed to enable tracing.  */
+  if (tp->btrace.target == NULL)
+    return;
+
+  /* We need to undo the enable in case of errors.  */
+  TRY
+    {
+      /* Add an entry for the current PC so we start tracing from where we
+        enabled it.
+
+        If we can't access TP's registers, TP is most likely running.  In this
+        case, we can't really say where tracing was enabled so it should be
+        safe to simply skip this step.
+
+        This is not relevant for BTRACE_FORMAT_PT since the trace will already
+        start at the PC at which tracing was enabled.  */
+      if (conf->format != BTRACE_FORMAT_PT
+         && can_access_registers_ptid (tp->ptid))
+       btrace_add_pc (tp);
+    }
+  CATCH (exception, RETURN_MASK_ALL)
+    {
+      btrace_disable (tp);
+
+      throw_exception (exception);
+    }
+  END_CATCH
 }
 
 /* See btrace.h.  */
@@ -766,7 +1646,8 @@ btrace_disable (struct thread_info *tp)
   if (btp->target == NULL)
     return;
 
-  DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("disable thread %s (%s)", print_thread_id (tp),
+        target_pid_to_str (tp->ptid));
 
   target_disable_btrace (btp->target);
   btp->target = NULL;
@@ -785,7 +1666,8 @@ btrace_teardown (struct thread_info *tp)
   if (btp->target == NULL)
     return;
 
-  DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("teardown thread %s (%s)", print_thread_id (tp),
+        target_pid_to_str (tp->ptid));
 
   target_teardown_btrace (btp->target);
   btp->target = NULL;
@@ -796,20 +1678,31 @@ btrace_teardown (struct thread_info *tp)
 /* Stitch branch trace in BTS format.  */
 
 static int
-btrace_stitch_bts (struct btrace_data_bts *btrace,
-                  const struct btrace_thread_info *btinfo)
+btrace_stitch_bts (struct btrace_data_bts *btrace, struct thread_info *tp)
 {
+  struct btrace_thread_info *btinfo;
   struct btrace_function *last_bfun;
   struct btrace_insn *last_insn;
   btrace_block_s *first_new_block;
 
-  last_bfun = btinfo->end;
-  gdb_assert (last_bfun != NULL);
+  btinfo = &tp->btrace;
+  gdb_assert (!btinfo->functions.empty ());
+  gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks));
+
+  last_bfun = &btinfo->functions.back ();
+
+  /* If the existing trace ends with a gap, we just glue the traces
+     together.  We need to drop the last (i.e. chronologically first) block
+     of the new trace,  though, since we can't fill in the start address.*/
+  if (VEC_empty (btrace_insn_s, last_bfun->insn))
+    {
+      VEC_pop (btrace_block_s, btrace->blocks);
+      return 0;
+    }
 
   /* Beware that block trace starts with the most recent block, so the
      chronologically first block in the new trace is the last block in
      the new trace's block vector.  */
-  gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks));
   first_new_block = VEC_last (btrace_block_s, btrace->blocks);
   last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
 
@@ -857,18 +1750,25 @@ btrace_stitch_bts (struct btrace_data_bts *btrace,
      been the only instruction in this function segment.
      This violates the invariant but will be remedied shortly by
      btrace_compute_ftrace when we add the new trace.  */
+
+  /* The only case where this would hurt is if the entire trace consisted
+     of just that one instruction.  If we remove it, we might turn the now
+     empty btrace function segment into a gap.  But we don't want gaps at
+     the beginning.  To avoid this, we remove the entire old trace.  */
+  if (last_bfun->number == 1 && VEC_empty (btrace_insn_s, last_bfun->insn))
+    btrace_clear (tp);
+
   return 0;
 }
 
 /* Adjust the block trace in order to stitch old and new trace together.
    BTRACE is the new delta trace between the last and the current stop.
-   BTINFO is the old branch trace until the last stop.
-   May modifx BTRACE as well as the existing trace in BTINFO.
+   TP is the traced thread.
+   May modifx BTRACE as well as the existing trace in TP.
    Return 0 on success, -1 otherwise.  */
 
 static int
-btrace_stitch_trace (struct btrace_data *btrace,
-                    const struct btrace_thread_info *btinfo)
+btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp)
 {
   /* If we don't have trace, there's nothing to do.  */
   if (btrace_data_empty (btrace))
@@ -880,7 +1780,11 @@ btrace_stitch_trace (struct btrace_data *btrace,
       return 0;
 
     case BTRACE_FORMAT_BTS:
-      return btrace_stitch_bts (&btrace->variant.bts, btinfo);
+      return btrace_stitch_bts (&btrace->variant.bts, tp);
+
+    case BTRACE_FORMAT_PT:
+      /* Delta reads are not supported.  */
+      return -1;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -900,6 +1804,82 @@ btrace_clear_history (struct btrace_thread_info *btinfo)
   btinfo->replay = NULL;
 }
 
+/* Clear the branch trace maintenance histories in BTINFO.  */
+
+static void
+btrace_maint_clear (struct btrace_thread_info *btinfo)
+{
+  switch (btinfo->data.format)
+    {
+    default:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      btinfo->maint.variant.bts.packet_history.begin = 0;
+      btinfo->maint.variant.bts.packet_history.end = 0;
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      xfree (btinfo->maint.variant.pt.packets);
+
+      btinfo->maint.variant.pt.packets = NULL;
+      btinfo->maint.variant.pt.packet_history.begin = 0;
+      btinfo->maint.variant.pt.packet_history.end = 0;
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+    }
+}
+
+/* See btrace.h.  */
+
+const char *
+btrace_decode_error (enum btrace_format format, int errcode)
+{
+  switch (format)
+    {
+    case BTRACE_FORMAT_BTS:
+      switch (errcode)
+       {
+       case BDE_BTS_OVERFLOW:
+         return _("instruction overflow");
+
+       case BDE_BTS_INSN_SIZE:
+         return _("unknown instruction");
+
+       default:
+         break;
+       }
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      switch (errcode)
+       {
+       case BDE_PT_USER_QUIT:
+         return _("trace decode cancelled");
+
+       case BDE_PT_DISABLED:
+         return _("disabled");
+
+       case BDE_PT_OVERFLOW:
+         return _("overflow");
+
+       default:
+         if (errcode < 0)
+           return pt_errstr (pt_errcode (errcode));
+         break;
+       }
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+
+    default:
+      break;
+    }
+
+  return _("unknown");
+}
+
 /* See btrace.h.  */
 
 void
@@ -911,7 +1891,8 @@ btrace_fetch (struct thread_info *tp)
   struct cleanup *cleanup;
   int errcode;
 
-  DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("fetch thread %s (%s)", print_thread_id (tp),
+        target_pid_to_str (tp->ptid));
 
   btinfo = &tp->btrace;
   tinfo = btinfo->target;
@@ -924,17 +1905,26 @@ btrace_fetch (struct thread_info *tp)
   if (btinfo->replay != NULL)
     return;
 
+  /* With CLI usage, TP->PTID always equals INFERIOR_PTID here.  Now that we
+     can store a gdb.Record object in Python referring to a different thread
+     than the current one, temporarily set INFERIOR_PTID.  */
+  scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
+  inferior_ptid = tp->ptid;
+
+  /* We should not be called on running or exited threads.  */
+  gdb_assert (can_access_registers_ptid (tp->ptid));
+
   btrace_data_init (&btrace);
   cleanup = make_cleanup_btrace_data (&btrace);
 
   /* Let's first try to extend the trace we already have.  */
-  if (btinfo->end != NULL)
+  if (!btinfo->functions.empty ())
     {
       errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_DELTA);
       if (errcode == 0)
        {
          /* Success.  Let's try to stitch the traces together.  */
-         errcode = btrace_stitch_trace (&btrace, btinfo);
+         errcode = btrace_stitch_trace (&btrace, tp);
        }
       else
        {
@@ -963,8 +1953,13 @@ btrace_fetch (struct thread_info *tp)
   /* Compute the trace, provided we have any.  */
   if (!btrace_data_empty (&btrace))
     {
+      /* Store the raw trace data.  The stored data will be cleared in
+        btrace_clear, so we always append the new trace.  */
+      btrace_data_append (&btinfo->data, &btrace);
+      btrace_maint_clear (btinfo);
+
       btrace_clear_history (btinfo);
-      btrace_compute_ftrace (btinfo, &btrace);
+      btrace_compute_ftrace (tp, &btrace);
     }
 
   do_cleanups (cleanup);
@@ -976,28 +1971,24 @@ void
 btrace_clear (struct thread_info *tp)
 {
   struct btrace_thread_info *btinfo;
-  struct btrace_function *it, *trash;
 
-  DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("clear thread %s (%s)", print_thread_id (tp),
+        target_pid_to_str (tp->ptid));
 
   /* Make sure btrace frames that may hold a pointer into the branch
      trace data are destroyed.  */
   reinit_frame_cache ();
 
   btinfo = &tp->btrace;
+  for (auto &bfun : btinfo->functions)
+    VEC_free (btrace_insn_s, bfun.insn);
 
-  it = btinfo->begin;
-  while (it != NULL)
-    {
-      trash = it;
-      it = it->flow.next;
-
-      xfree (trash);
-    }
-
-  btinfo->begin = NULL;
-  btinfo->end = NULL;
+  btinfo->functions.clear ();
+  btinfo->ngaps = 0;
 
+  /* Must clear the maint data before - it depends on BTINFO->DATA.  */
+  btrace_maint_clear (btinfo);
+  btrace_data_clear (&btinfo->data);
   btrace_clear_history (btinfo);
 }
 
@@ -1023,7 +2014,8 @@ check_xml_btrace_version (struct gdb_xml_parser *parser,
                          const struct gdb_xml_element *element,
                          void *user_data, VEC (gdb_xml_value_s) *attributes)
 {
-  const char *version = xml_find_attribute (attributes, "version")->value;
+  const char *version
+    = (const char *) xml_find_attribute (attributes, "version")->value;
 
   if (strcmp (version, "1.0") != 0)
     gdb_xml_error (parser, _("Unsupported btrace version: \"%s\""), version);
@@ -1040,7 +2032,7 @@ parse_xml_btrace_block (struct gdb_xml_parser *parser,
   struct btrace_block *block;
   ULONGEST *begin, *end;
 
-  btrace = user_data;
+  btrace = (struct btrace_data *) user_data;
 
   switch (btrace->format)
     {
@@ -1056,20 +2048,138 @@ parse_xml_btrace_block (struct gdb_xml_parser *parser,
       gdb_xml_error (parser, _("Btrace format error."));
     }
 
-  begin = xml_find_attribute (attributes, "begin")->value;
-  end = xml_find_attribute (attributes, "end")->value;
+  begin = (ULONGEST *) xml_find_attribute (attributes, "begin")->value;
+  end = (ULONGEST *) xml_find_attribute (attributes, "end")->value;
 
   block = VEC_safe_push (btrace_block_s, btrace->variant.bts.blocks, NULL);
   block->begin = *begin;
   block->end = *end;
 }
 
+/* Parse a "raw" xml record.  */
+
+static void
+parse_xml_raw (struct gdb_xml_parser *parser, const char *body_text,
+              gdb_byte **pdata, size_t *psize)
+{
+  struct cleanup *cleanup;
+  gdb_byte *data, *bin;
+  size_t len, size;
+
+  len = strlen (body_text);
+  if (len % 2 != 0)
+    gdb_xml_error (parser, _("Bad raw data size."));
+
+  size = len / 2;
+
+  bin = data = (gdb_byte *) xmalloc (size);
+  cleanup = make_cleanup (xfree, data);
+
+  /* We use hex encoding - see common/rsp-low.h.  */
+  while (len > 0)
+    {
+      char hi, lo;
+
+      hi = *body_text++;
+      lo = *body_text++;
+
+      if (hi == 0 || lo == 0)
+       gdb_xml_error (parser, _("Bad hex encoding."));
+
+      *bin++ = fromhex (hi) * 16 + fromhex (lo);
+      len -= 2;
+    }
+
+  discard_cleanups (cleanup);
+
+  *pdata = data;
+  *psize = size;
+}
+
+/* Parse a btrace pt-config "cpu" xml record.  */
+
+static void
+parse_xml_btrace_pt_config_cpu (struct gdb_xml_parser *parser,
+                               const struct gdb_xml_element *element,
+                               void *user_data,
+                               VEC (gdb_xml_value_s) *attributes)
+{
+  struct btrace_data *btrace;
+  const char *vendor;
+  ULONGEST *family, *model, *stepping;
+
+  vendor = (const char *) xml_find_attribute (attributes, "vendor")->value;
+  family = (ULONGEST *) xml_find_attribute (attributes, "family")->value;
+  model = (ULONGEST *) xml_find_attribute (attributes, "model")->value;
+  stepping = (ULONGEST *) xml_find_attribute (attributes, "stepping")->value;
+
+  btrace = (struct btrace_data *) user_data;
+
+  if (strcmp (vendor, "GenuineIntel") == 0)
+    btrace->variant.pt.config.cpu.vendor = CV_INTEL;
+
+  btrace->variant.pt.config.cpu.family = *family;
+  btrace->variant.pt.config.cpu.model = *model;
+  btrace->variant.pt.config.cpu.stepping = *stepping;
+}
+
+/* Parse a btrace pt "raw" xml record.  */
+
+static void
+parse_xml_btrace_pt_raw (struct gdb_xml_parser *parser,
+                        const struct gdb_xml_element *element,
+                        void *user_data, const char *body_text)
+{
+  struct btrace_data *btrace;
+
+  btrace = (struct btrace_data *) user_data;
+  parse_xml_raw (parser, body_text, &btrace->variant.pt.data,
+                &btrace->variant.pt.size);
+}
+
+/* Parse a btrace "pt" xml record.  */
+
+static void
+parse_xml_btrace_pt (struct gdb_xml_parser *parser,
+                    const struct gdb_xml_element *element,
+                    void *user_data, VEC (gdb_xml_value_s) *attributes)
+{
+  struct btrace_data *btrace;
+
+  btrace = (struct btrace_data *) user_data;
+  btrace->format = BTRACE_FORMAT_PT;
+  btrace->variant.pt.config.cpu.vendor = CV_UNKNOWN;
+  btrace->variant.pt.data = NULL;
+  btrace->variant.pt.size = 0;
+}
+
 static const struct gdb_xml_attribute block_attributes[] = {
   { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
   { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
+static const struct gdb_xml_attribute btrace_pt_config_cpu_attributes[] = {
+  { "vendor", GDB_XML_AF_NONE, NULL, NULL },
+  { "family", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "model", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "stepping", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element btrace_pt_config_children[] = {
+  { "cpu", btrace_pt_config_cpu_attributes, NULL, GDB_XML_EF_OPTIONAL,
+    parse_xml_btrace_pt_config_cpu, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element btrace_pt_children[] = {
+  { "pt-config", NULL, btrace_pt_config_children, GDB_XML_EF_OPTIONAL, NULL,
+    NULL },
+  { "raw", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL, parse_xml_btrace_pt_raw },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
 static const struct gdb_xml_attribute btrace_attributes[] = {
   { "version", GDB_XML_AF_NONE, NULL, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
@@ -1078,6 +2188,8 @@ static const struct gdb_xml_attribute btrace_attributes[] = {
 static const struct gdb_xml_element btrace_children[] = {
   { "block", block_attributes, NULL,
     GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, parse_xml_btrace_block, NULL },
+  { "pt", NULL, btrace_pt_children, GDB_XML_EF_OPTIONAL, parse_xml_btrace_pt,
+    NULL },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -1127,13 +2239,51 @@ parse_xml_btrace_conf_bts (struct gdb_xml_parser *parser,
                          void *user_data, VEC (gdb_xml_value_s) *attributes)
 {
   struct btrace_config *conf;
+  struct gdb_xml_value *size;
 
-  conf = user_data;
+  conf = (struct btrace_config *) user_data;
   conf->format = BTRACE_FORMAT_BTS;
+  conf->bts.size = 0;
+
+  size = xml_find_attribute (attributes, "size");
+  if (size != NULL)
+    conf->bts.size = (unsigned int) *(ULONGEST *) size->value;
+}
+
+/* Parse a btrace-conf "pt" xml record.  */
+
+static void
+parse_xml_btrace_conf_pt (struct gdb_xml_parser *parser,
+                         const struct gdb_xml_element *element,
+                         void *user_data, VEC (gdb_xml_value_s) *attributes)
+{
+  struct btrace_config *conf;
+  struct gdb_xml_value *size;
+
+  conf = (struct btrace_config *) user_data;
+  conf->format = BTRACE_FORMAT_PT;
+  conf->pt.size = 0;
+
+  size = xml_find_attribute (attributes, "size");
+  if (size != NULL)
+    conf->pt.size = (unsigned int) *(ULONGEST *) size->value;
 }
 
+static const struct gdb_xml_attribute btrace_conf_pt_attributes[] = {
+  { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute btrace_conf_bts_attributes[] = {
+  { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
 static const struct gdb_xml_element btrace_conf_children[] = {
-  { "bts", NULL, NULL, GDB_XML_EF_OPTIONAL, parse_xml_btrace_conf_bts, NULL },
+  { "bts", btrace_conf_bts_attributes, NULL, GDB_XML_EF_OPTIONAL,
+    parse_xml_btrace_conf_bts, NULL },
+  { "pt", btrace_conf_pt_attributes, NULL, GDB_XML_EF_OPTIONAL,
+    parse_xml_btrace_conf_pt, NULL },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -1179,8 +2329,12 @@ btrace_insn_get (const struct btrace_insn_iterator *it)
   const struct btrace_function *bfun;
   unsigned int index, end;
 
-  index = it->index;
-  bfun = it->function;
+  index = it->insn_index;
+  bfun = &it->btinfo->functions[it->call_index];
+
+  /* Check if the iterator points to a gap in the trace.  */
+  if (bfun->errcode != 0)
+    return NULL;
 
   /* The index is within the bounds of this function's instruction vector.  */
   end = VEC_length (btrace_insn_s, bfun->insn);
@@ -1192,13 +2346,18 @@ btrace_insn_get (const struct btrace_insn_iterator *it)
 
 /* See btrace.h.  */
 
+int
+btrace_insn_get_error (const struct btrace_insn_iterator *it)
+{
+  return it->btinfo->functions[it->call_index].errcode;
+}
+
+/* See btrace.h.  */
+
 unsigned int
 btrace_insn_number (const struct btrace_insn_iterator *it)
 {
-  const struct btrace_function *bfun;
-
-  bfun = it->function;
-  return bfun->insn_offset + it->index;
+  return it->btinfo->functions[it->call_index].insn_offset + it->insn_index;
 }
 
 /* See btrace.h.  */
@@ -1207,14 +2366,12 @@ void
 btrace_insn_begin (struct btrace_insn_iterator *it,
                   const struct btrace_thread_info *btinfo)
 {
-  const struct btrace_function *bfun;
-
-  bfun = btinfo->begin;
-  if (bfun == NULL)
+  if (btinfo->functions.empty ())
     error (_("No trace."));
 
-  it->function = bfun;
-  it->index = 0;
+  it->btinfo = btinfo;
+  it->call_index = 0;
+  it->insn_index = 0;
 }
 
 /* See btrace.h.  */
@@ -1226,16 +2383,21 @@ btrace_insn_end (struct btrace_insn_iterator *it,
   const struct btrace_function *bfun;
   unsigned int length;
 
-  bfun = btinfo->end;
-  if (bfun == NULL)
+  if (btinfo->functions.empty ())
     error (_("No trace."));
 
-  /* The last instruction in the last function is the current instruction.
-     We point to it - it is one past the end of the execution trace.  */
+  bfun = &btinfo->functions.back ();
   length = VEC_length (btrace_insn_s, bfun->insn);
 
-  it->function = bfun;
-  it->index = length - 1;
+  /* The last function may either be a gap or it contains the current
+     instruction, which is one past the end of the execution trace; ignore
+     it.  */
+  if (length > 0)
+    length -= 1;
+
+  it->btinfo = btinfo;
+  it->call_index = bfun->number - 1;
+  it->insn_index = length;
 }
 
 /* See btrace.h.  */
@@ -1246,9 +2408,9 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
   const struct btrace_function *bfun;
   unsigned int index, steps;
 
-  bfun = it->function;
+  bfun = &it->btinfo->functions[it->call_index];
   steps = 0;
-  index = it->index;
+  index = it->insn_index;
 
   while (stride != 0)
     {
@@ -1256,6 +2418,25 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
 
       end = VEC_length (btrace_insn_s, bfun->insn);
 
+      /* An empty function segment represents a gap in the trace.  We count
+        it as one instruction.  */
+      if (end == 0)
+       {
+         const struct btrace_function *next;
+
+         next = ftrace_find_call_by_number (it->btinfo, bfun->number + 1);
+         if (next == NULL)
+           break;
+
+         stride -= 1;
+         steps += 1;
+
+         bfun = next;
+         index = 0;
+
+         continue;
+       }
+
       gdb_assert (0 < end);
       gdb_assert (index < end);
 
@@ -1263,7 +2444,7 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
       space = end - index;
 
       /* Advance the iterator as far as possible within this segment.  */
-      adv = min (space, stride);
+      adv = std::min (space, stride);
       stride -= adv;
       index += adv;
       steps += adv;
@@ -1273,7 +2454,7 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
        {
          const struct btrace_function *next;
 
-         next = bfun->flow.next;
+         next = ftrace_find_call_by_number (it->btinfo, bfun->number + 1);
          if (next == NULL)
            {
              /* We stepped past the last function.
@@ -1295,8 +2476,8 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
     }
 
   /* Update the iterator.  */
-  it->function = bfun;
-  it->index = index;
+  it->call_index = bfun->number - 1;
+  it->insn_index = index;
 
   return steps;
 }
@@ -1309,9 +2490,9 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
   const struct btrace_function *bfun;
   unsigned int index, steps;
 
-  bfun = it->function;
+  bfun = &it->btinfo->functions[it->call_index];
   steps = 0;
-  index = it->index;
+  index = it->insn_index;
 
   while (stride != 0)
     {
@@ -1322,7 +2503,7 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
        {
          const struct btrace_function *prev;
 
-         prev = bfun->flow.prev;
+         prev = ftrace_find_call_by_number (it->btinfo, bfun->number - 1);
          if (prev == NULL)
            break;
 
@@ -1330,12 +2511,20 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
          bfun = prev;
          index = VEC_length (btrace_insn_s, bfun->insn);
 
-         /* There is at least one instruction in this function segment.  */
-         gdb_assert (index > 0);
+         /* An empty function segment represents a gap in the trace.  We count
+            it as one instruction.  */
+         if (index == 0)
+           {
+             stride -= 1;
+             steps += 1;
+
+             continue;
+           }
        }
 
       /* Advance the iterator as far as possible within this segment.  */
-      adv = min (index, stride);
+      adv = std::min (index, stride);
+
       stride -= adv;
       index -= adv;
       steps += adv;
@@ -1345,8 +2534,8 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
     }
 
   /* Update the iterator.  */
-  it->function = bfun;
-  it->index = index;
+  it->call_index = bfun->number - 1;
+  it->insn_index = index;
 
   return steps;
 }
@@ -1357,12 +2546,12 @@ int
 btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
                 const struct btrace_insn_iterator *rhs)
 {
-  unsigned int lnum, rnum;
+  gdb_assert (lhs->btinfo == rhs->btinfo);
 
-  lnum = btrace_insn_number (lhs);
-  rnum = btrace_insn_number (rhs);
+  if (lhs->call_index != rhs->call_index)
+    return lhs->call_index - rhs->call_index;
 
-  return (int) (lnum - rnum);
+  return lhs->insn_index - rhs->insn_index;
 }
 
 /* See btrace.h.  */
@@ -1373,31 +2562,76 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
                            unsigned int number)
 {
   const struct btrace_function *bfun;
-  unsigned int end;
+  unsigned int upper, lower;
 
-  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
-    if (bfun->insn_offset <= number)
-      break;
+  if (btinfo->functions.empty ())
+      return 0;
 
-  if (bfun == NULL)
+  lower = 0;
+  bfun = &btinfo->functions[lower];
+  if (number < bfun->insn_offset)
     return 0;
 
-  end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
-  if (end <= number)
+  upper = btinfo->functions.size () - 1;
+  bfun = &btinfo->functions[upper];
+  if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
     return 0;
 
-  it->function = bfun;
-  it->index = number - bfun->insn_offset;
+  /* We assume that there are no holes in the numbering.  */
+  for (;;)
+    {
+      const unsigned int average = lower + (upper - lower) / 2;
+
+      bfun = &btinfo->functions[average];
+
+      if (number < bfun->insn_offset)
+       {
+         upper = average - 1;
+         continue;
+       }
+
+      if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
+       {
+         lower = average + 1;
+         continue;
+       }
+
+      break;
+    }
 
+  it->btinfo = btinfo;
+  it->call_index = bfun->number - 1;
+  it->insn_index = number - bfun->insn_offset;
   return 1;
 }
 
+/* Returns true if the recording ends with a function segment that
+   contains only a single (i.e. the current) instruction.  */
+
+static bool
+btrace_ends_with_single_insn (const struct btrace_thread_info *btinfo)
+{
+  const btrace_function *bfun;
+
+  if (btinfo->functions.empty ())
+    return false;
+
+  bfun = &btinfo->functions.back ();
+  if (bfun->errcode != 0)
+    return false;
+
+  return ftrace_call_num_insn (bfun) == 1;
+}
+
 /* See btrace.h.  */
 
 const struct btrace_function *
 btrace_call_get (const struct btrace_call_iterator *it)
 {
-  return it->function;
+  if (it->index >= it->btinfo->functions.size ())
+    return NULL;
+
+  return &it->btinfo->functions[it->index];
 }
 
 /* See btrace.h.  */
@@ -1405,28 +2639,14 @@ btrace_call_get (const struct btrace_call_iterator *it)
 unsigned int
 btrace_call_number (const struct btrace_call_iterator *it)
 {
-  const struct btrace_thread_info *btinfo;
-  const struct btrace_function *bfun;
-  unsigned int insns;
+  const unsigned int length = it->btinfo->functions.size ();
 
-  btinfo = it->btinfo;
-  bfun = it->function;
-  if (bfun != NULL)
-    return bfun->number;
+  /* If the last function segment contains only a single instruction (i.e. the
+     current instruction), skip it.  */
+  if ((it->index == length) && btrace_ends_with_single_insn (it->btinfo))
+    return length;
 
-  /* For the end iterator, i.e. bfun == NULL, we return one more than the
-     number of the last function.  */
-  bfun = btinfo->end;
-  insns = VEC_length (btrace_insn_s, bfun->insn);
-
-  /* If the function contains only a single instruction (i.e. the current
-     instruction), it will be skipped and its number is already the number
-     we seek.  */
-  if (insns == 1)
-    return bfun->number;
-
-  /* Otherwise, return one more than the number of the last function.  */
-  return bfun->number + 1;
+  return it->index + 1;
 }
 
 /* See btrace.h.  */
@@ -1435,14 +2655,11 @@ void
 btrace_call_begin (struct btrace_call_iterator *it,
                   const struct btrace_thread_info *btinfo)
 {
-  const struct btrace_function *bfun;
-
-  bfun = btinfo->begin;
-  if (bfun == NULL)
+  if (btinfo->functions.empty ())
     error (_("No trace."));
 
   it->btinfo = btinfo;
-  it->function = bfun;
+  it->index = 0;
 }
 
 /* See btrace.h.  */
@@ -1451,14 +2668,11 @@ void
 btrace_call_end (struct btrace_call_iterator *it,
                 const struct btrace_thread_info *btinfo)
 {
-  const struct btrace_function *bfun;
-
-  bfun = btinfo->end;
-  if (bfun == NULL)
+  if (btinfo->functions.empty ())
     error (_("No trace."));
 
   it->btinfo = btinfo;
-  it->function = NULL;
+  it->index = btinfo->functions.size ();
 }
 
 /* See btrace.h.  */
@@ -1466,35 +2680,35 @@ btrace_call_end (struct btrace_call_iterator *it,
 unsigned int
 btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
 {
-  const struct btrace_function *bfun;
-  unsigned int steps;
+  const unsigned int length = it->btinfo->functions.size ();
 
-  bfun = it->function;
-  steps = 0;
-  while (bfun != NULL)
+  if (it->index + stride < length - 1)
+    /* Default case: Simply advance the iterator.  */
+    it->index += stride;
+  else if (it->index + stride == length - 1)
     {
-      const struct btrace_function *next;
-      unsigned int insns;
-
-      next = bfun->flow.next;
-      if (next == NULL)
-       {
-         /* Ignore the last function if it only contains a single
-            (i.e. the current) instruction.  */
-         insns = VEC_length (btrace_insn_s, bfun->insn);
-         if (insns == 1)
-           steps -= 1;
-       }
-
-      if (stride == steps)
-       break;
+      /* We land exactly at the last function segment.  If it contains only one
+        instruction (i.e. the current instruction) it is not actually part of
+        the trace.  */
+      if (btrace_ends_with_single_insn (it->btinfo))
+       it->index = length;
+      else
+       it->index = length - 1;
+    }
+  else
+    {
+      /* We land past the last function segment and have to adjust the stride.
+        If the last function segment contains only one instruction (i.e. the
+        current instruction) it is not actually part of the trace.  */
+      if (btrace_ends_with_single_insn (it->btinfo))
+       stride = length - it->index - 1;
+      else
+       stride = length - it->index;
 
-      bfun = next;
-      steps += 1;
+      it->index = length;
     }
 
-  it->function = bfun;
-  return steps;
+  return stride;
 }
 
 /* See btrace.h.  */
@@ -1502,48 +2716,33 @@ btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
 unsigned int
 btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride)
 {
-  const struct btrace_thread_info *btinfo;
-  const struct btrace_function *bfun;
-  unsigned int steps;
-
-  bfun = it->function;
-  steps = 0;
+  const unsigned int length = it->btinfo->functions.size ();
+  int steps = 0;
 
-  if (bfun == NULL)
-    {
-      unsigned int insns;
-
-      btinfo = it->btinfo;
-      bfun = btinfo->end;
-      if (bfun == NULL)
-       return 0;
-
-      /* Ignore the last function if it only contains a single
-        (i.e. the current) instruction.  */
-      insns = VEC_length (btrace_insn_s, bfun->insn);
-      if (insns == 1)
-       bfun = bfun->flow.prev;
-
-      if (bfun == NULL)
-       return 0;
+  gdb_assert (it->index <= length);
 
-      steps += 1;
-    }
+  if (stride == 0 || it->index == 0)
+    return 0;
 
-  while (steps < stride)
+  /* If we are at the end, the first step is a special case.  If the last
+     function segment contains only one instruction (i.e. the current
+     instruction) it is not actually part of the trace.  To be able to step
+     over this instruction, we need at least one more function segment.  */
+  if ((it->index == length)  && (length > 1))
     {
-      const struct btrace_function *prev;
-
-      prev = bfun->flow.prev;
-      if (prev == NULL)
-       break;
+      if (btrace_ends_with_single_insn (it->btinfo))
+       it->index = length - 2;
+      else
+       it->index = length - 1;
 
-      bfun = prev;
-      steps += 1;
+      steps = 1;
+      stride -= 1;
     }
 
-  it->function = bfun;
-  return steps;
+  stride = std::min (stride, it->index);
+
+  it->index -= stride;
+  return steps + stride;
 }
 
 /* See btrace.h.  */
@@ -1552,12 +2751,8 @@ int
 btrace_call_cmp (const struct btrace_call_iterator *lhs,
                 const struct btrace_call_iterator *rhs)
 {
-  unsigned int lnum, rnum;
-
-  lnum = btrace_call_number (lhs);
-  rnum = btrace_call_number (rhs);
-
-  return (int) (lnum - rnum);
+  gdb_assert (lhs->btinfo == rhs->btinfo);
+  return (int) (lhs->index - rhs->index);
 }
 
 /* See btrace.h.  */
@@ -1567,26 +2762,14 @@ btrace_find_call_by_number (struct btrace_call_iterator *it,
                            const struct btrace_thread_info *btinfo,
                            unsigned int number)
 {
-  const struct btrace_function *bfun;
-
-  for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
-    {
-      unsigned int bnum;
-
-      bnum = bfun->number;
-      if (number == bnum)
-       {
-         it->btinfo = btinfo;
-         it->function = bfun;
-         return 1;
-       }
+  const unsigned int length = btinfo->functions.size ();
 
-      /* Functions are ordered and numbered consecutively.  We could bail out
-        earlier.  On the other hand, it is very unlikely that we search for
-        a nonexistent function.  */
-  }
+  if ((number == 0) || (number > length))
+    return 0;
 
-  return 0;
+  it->btinfo = btinfo;
+  it->index = number - 1;
+  return 1;
 }
 
 /* See btrace.h.  */
@@ -1597,7 +2780,7 @@ btrace_set_insn_history (struct btrace_thread_info *btinfo,
                         const struct btrace_insn_iterator *end)
 {
   if (btinfo->insn_history == NULL)
-    btinfo->insn_history = xzalloc (sizeof (*btinfo->insn_history));
+    btinfo->insn_history = XCNEW (struct btrace_insn_history);
 
   btinfo->insn_history->begin = *begin;
   btinfo->insn_history->end = *end;
@@ -1613,7 +2796,7 @@ btrace_set_call_history (struct btrace_thread_info *btinfo,
   gdb_assert (begin->btinfo == end->btinfo);
 
   if (btinfo->call_history == NULL)
-    btinfo->call_history = xzalloc (sizeof (*btinfo->call_history));
+    btinfo->call_history = XCNEW (struct btrace_call_history);
 
   btinfo->call_history->begin = *begin;
   btinfo->call_history->end = *end;
@@ -1637,7 +2820,7 @@ btrace_is_empty (struct thread_info *tp)
 
   btinfo = &tp->btrace;
 
-  if (btinfo->begin == NULL)
+  if (btinfo->functions.empty ())
     return 1;
 
   btrace_insn_begin (&begin, btinfo);
@@ -1651,7 +2834,7 @@ btrace_is_empty (struct thread_info *tp)
 static void
 do_btrace_data_cleanup (void *arg)
 {
-  btrace_data_fini (arg);
+  btrace_data_fini ((struct btrace_data *) arg);
 }
 
 /* See btrace.h.  */
@@ -1661,3 +2844,718 @@ make_cleanup_btrace_data (struct btrace_data *data)
 {
   return make_cleanup (do_btrace_data_cleanup, data);
 }
+
+#if defined (HAVE_LIBIPT)
+
+/* Print a single packet.  */
+
+static void
+pt_print_packet (const struct pt_packet *packet)
+{
+  switch (packet->type)
+    {
+    default:
+      printf_unfiltered (("[??: %x]"), packet->type);
+      break;
+
+    case ppt_psb:
+      printf_unfiltered (("psb"));
+      break;
+
+    case ppt_psbend:
+      printf_unfiltered (("psbend"));
+      break;
+
+    case ppt_pad:
+      printf_unfiltered (("pad"));
+      break;
+
+    case ppt_tip:
+      printf_unfiltered (("tip %u: 0x%" PRIx64 ""),
+                        packet->payload.ip.ipc,
+                        packet->payload.ip.ip);
+      break;
+
+    case ppt_tip_pge:
+      printf_unfiltered (("tip.pge %u: 0x%" PRIx64 ""),
+                        packet->payload.ip.ipc,
+                        packet->payload.ip.ip);
+      break;
+
+    case ppt_tip_pgd:
+      printf_unfiltered (("tip.pgd %u: 0x%" PRIx64 ""),
+                        packet->payload.ip.ipc,
+                        packet->payload.ip.ip);
+      break;
+
+    case ppt_fup:
+      printf_unfiltered (("fup %u: 0x%" PRIx64 ""),
+                        packet->payload.ip.ipc,
+                        packet->payload.ip.ip);
+      break;
+
+    case ppt_tnt_8:
+      printf_unfiltered (("tnt-8 %u: 0x%" PRIx64 ""),
+                        packet->payload.tnt.bit_size,
+                        packet->payload.tnt.payload);
+      break;
+
+    case ppt_tnt_64:
+      printf_unfiltered (("tnt-64 %u: 0x%" PRIx64 ""),
+                        packet->payload.tnt.bit_size,
+                        packet->payload.tnt.payload);
+      break;
+
+    case ppt_pip:
+      printf_unfiltered (("pip %" PRIx64 "%s"), packet->payload.pip.cr3,
+                        packet->payload.pip.nr ? (" nr") : (""));
+      break;
+
+    case ppt_tsc:
+      printf_unfiltered (("tsc %" PRIx64 ""), packet->payload.tsc.tsc);
+      break;
+
+    case ppt_cbr:
+      printf_unfiltered (("cbr %u"), packet->payload.cbr.ratio);
+      break;
+
+    case ppt_mode:
+      switch (packet->payload.mode.leaf)
+       {
+       default:
+         printf_unfiltered (("mode %u"), packet->payload.mode.leaf);
+         break;
+
+       case pt_mol_exec:
+         printf_unfiltered (("mode.exec%s%s"),
+                            packet->payload.mode.bits.exec.csl
+                            ? (" cs.l") : (""),
+                            packet->payload.mode.bits.exec.csd
+                            ? (" cs.d") : (""));
+         break;
+
+       case pt_mol_tsx:
+         printf_unfiltered (("mode.tsx%s%s"),
+                            packet->payload.mode.bits.tsx.intx
+                            ? (" intx") : (""),
+                            packet->payload.mode.bits.tsx.abrt
+                            ? (" abrt") : (""));
+         break;
+       }
+      break;
+
+    case ppt_ovf:
+      printf_unfiltered (("ovf"));
+      break;
+
+    case ppt_stop:
+      printf_unfiltered (("stop"));
+      break;
+
+    case ppt_vmcs:
+      printf_unfiltered (("vmcs %" PRIx64 ""), packet->payload.vmcs.base);
+      break;
+
+    case ppt_tma:
+      printf_unfiltered (("tma %x %x"), packet->payload.tma.ctc,
+                        packet->payload.tma.fc);
+      break;
+
+    case ppt_mtc:
+      printf_unfiltered (("mtc %x"), packet->payload.mtc.ctc);
+      break;
+
+    case ppt_cyc:
+      printf_unfiltered (("cyc %" PRIx64 ""), packet->payload.cyc.value);
+      break;
+
+    case ppt_mnt:
+      printf_unfiltered (("mnt %" PRIx64 ""), packet->payload.mnt.payload);
+      break;
+    }
+}
+
+/* Decode packets into MAINT using DECODER.  */
+
+static void
+btrace_maint_decode_pt (struct btrace_maint_info *maint,
+                       struct pt_packet_decoder *decoder)
+{
+  int errcode;
+
+  for (;;)
+    {
+      struct btrace_pt_packet packet;
+
+      errcode = pt_pkt_sync_forward (decoder);
+      if (errcode < 0)
+       break;
+
+      for (;;)
+       {
+         pt_pkt_get_offset (decoder, &packet.offset);
+
+         errcode = pt_pkt_next (decoder, &packet.packet,
+                                sizeof(packet.packet));
+         if (errcode < 0)
+           break;
+
+         if (maint_btrace_pt_skip_pad == 0 || packet.packet.type != ppt_pad)
+           {
+             packet.errcode = pt_errcode (errcode);
+             VEC_safe_push (btrace_pt_packet_s, maint->variant.pt.packets,
+                            &packet);
+           }
+       }
+
+      if (errcode == -pte_eos)
+       break;
+
+      packet.errcode = pt_errcode (errcode);
+      VEC_safe_push (btrace_pt_packet_s, maint->variant.pt.packets,
+                    &packet);
+
+      warning (_("Error at trace offset 0x%" PRIx64 ": %s."),
+              packet.offset, pt_errstr (packet.errcode));
+    }
+
+  if (errcode != -pte_eos)
+    warning (_("Failed to synchronize onto the Intel Processor Trace "
+              "stream: %s."), pt_errstr (pt_errcode (errcode)));
+}
+
+/* Update the packet history in BTINFO.  */
+
+static void
+btrace_maint_update_pt_packets (struct btrace_thread_info *btinfo)
+{
+  volatile struct gdb_exception except;
+  struct pt_packet_decoder *decoder;
+  struct btrace_data_pt *pt;
+  struct pt_config config;
+  int errcode;
+
+  pt = &btinfo->data.variant.pt;
+
+  /* Nothing to do if there is no trace.  */
+  if (pt->size == 0)
+    return;
+
+  memset (&config, 0, sizeof(config));
+
+  config.size = sizeof (config);
+  config.begin = pt->data;
+  config.end = pt->data + pt->size;
+
+  config.cpu.vendor = pt_translate_cpu_vendor (pt->config.cpu.vendor);
+  config.cpu.family = pt->config.cpu.family;
+  config.cpu.model = pt->config.cpu.model;
+  config.cpu.stepping = pt->config.cpu.stepping;
+
+  errcode = pt_cpu_errata (&config.errata, &config.cpu);
+  if (errcode < 0)
+    error (_("Failed to configure the Intel Processor Trace decoder: %s."),
+          pt_errstr (pt_errcode (errcode)));
+
+  decoder = pt_pkt_alloc_decoder (&config);
+  if (decoder == NULL)
+    error (_("Failed to allocate the Intel Processor Trace decoder."));
+
+  TRY
+    {
+      btrace_maint_decode_pt (&btinfo->maint, decoder);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      pt_pkt_free_decoder (decoder);
+
+      if (except.reason < 0)
+       throw_exception (except);
+    }
+  END_CATCH
+
+  pt_pkt_free_decoder (decoder);
+}
+
+#endif /* !defined (HAVE_LIBIPT)  */
+
+/* Update the packet maintenance information for BTINFO and store the
+   low and high bounds into BEGIN and END, respectively.
+   Store the current iterator state into FROM and TO.  */
+
+static void
+btrace_maint_update_packets (struct btrace_thread_info *btinfo,
+                            unsigned int *begin, unsigned int *end,
+                            unsigned int *from, unsigned int *to)
+{
+  switch (btinfo->data.format)
+    {
+    default:
+      *begin = 0;
+      *end = 0;
+      *from = 0;
+      *to = 0;
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      /* Nothing to do - we operate directly on BTINFO->DATA.  */
+      *begin = 0;
+      *end = VEC_length (btrace_block_s, btinfo->data.variant.bts.blocks);
+      *from = btinfo->maint.variant.bts.packet_history.begin;
+      *to = btinfo->maint.variant.bts.packet_history.end;
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      if (VEC_empty (btrace_pt_packet_s, btinfo->maint.variant.pt.packets))
+       btrace_maint_update_pt_packets (btinfo);
+
+      *begin = 0;
+      *end = VEC_length (btrace_pt_packet_s, btinfo->maint.variant.pt.packets);
+      *from = btinfo->maint.variant.pt.packet_history.begin;
+      *to = btinfo->maint.variant.pt.packet_history.end;
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+    }
+}
+
+/* Print packets in BTINFO from BEGIN (inclusive) until END (exclusive) and
+   update the current iterator position.  */
+
+static void
+btrace_maint_print_packets (struct btrace_thread_info *btinfo,
+                           unsigned int begin, unsigned int end)
+{
+  switch (btinfo->data.format)
+    {
+    default:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      {
+       VEC (btrace_block_s) *blocks;
+       unsigned int blk;
+
+       blocks = btinfo->data.variant.bts.blocks;
+       for (blk = begin; blk < end; ++blk)
+         {
+           const btrace_block_s *block;
+
+           block = VEC_index (btrace_block_s, blocks, blk);
+
+           printf_unfiltered ("%u\tbegin: %s, end: %s\n", blk,
+                              core_addr_to_string_nz (block->begin),
+                              core_addr_to_string_nz (block->end));
+         }
+
+       btinfo->maint.variant.bts.packet_history.begin = begin;
+       btinfo->maint.variant.bts.packet_history.end = end;
+      }
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      {
+       VEC (btrace_pt_packet_s) *packets;
+       unsigned int pkt;
+
+       packets = btinfo->maint.variant.pt.packets;
+       for (pkt = begin; pkt < end; ++pkt)
+         {
+           const struct btrace_pt_packet *packet;
+
+           packet = VEC_index (btrace_pt_packet_s, packets, pkt);
+
+           printf_unfiltered ("%u\t", pkt);
+           printf_unfiltered ("0x%" PRIx64 "\t", packet->offset);
+
+           if (packet->errcode == pte_ok)
+             pt_print_packet (&packet->packet);
+           else
+             printf_unfiltered ("[error: %s]", pt_errstr (packet->errcode));
+
+           printf_unfiltered ("\n");
+         }
+
+       btinfo->maint.variant.pt.packet_history.begin = begin;
+       btinfo->maint.variant.pt.packet_history.end = end;
+      }
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+    }
+}
+
+/* Read a number from an argument string.  */
+
+static unsigned int
+get_uint (char **arg)
+{
+  char *begin, *end, *pos;
+  unsigned long number;
+
+  begin = *arg;
+  pos = skip_spaces (begin);
+
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  number = strtoul (pos, &end, 10);
+  if (number > UINT_MAX)
+    error (_("Number too big."));
+
+  *arg += (end - begin);
+
+  return (unsigned int) number;
+}
+
+/* Read a context size from an argument string.  */
+
+static int
+get_context_size (char **arg)
+{
+  char *pos;
+  int number;
+
+  pos = skip_spaces (*arg);
+
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  return strtol (pos, arg, 10);
+}
+
+/* Complain about junk at the end of an argument string.  */
+
+static void
+no_chunk (char *arg)
+{
+  if (*arg != 0)
+    error (_("Junk after argument: %s."), arg);
+}
+
+/* The "maintenance btrace packet-history" command.  */
+
+static void
+maint_btrace_packet_history_cmd (char *arg, int from_tty)
+{
+  struct btrace_thread_info *btinfo;
+  struct thread_info *tp;
+  unsigned int size, begin, end, from, to;
+
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp == NULL)
+    error (_("No thread."));
+
+  size = 10;
+  btinfo = &tp->btrace;
+
+  btrace_maint_update_packets (btinfo, &begin, &end, &from, &to);
+  if (begin == end)
+    {
+      printf_unfiltered (_("No trace.\n"));
+      return;
+    }
+
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    {
+      from = to;
+
+      if (end - from < size)
+       size = end - from;
+      to = from + size;
+    }
+  else if (strcmp (arg, "-") == 0)
+    {
+      to = from;
+
+      if (to - begin < size)
+       size = to - begin;
+      from = to - size;
+    }
+  else
+    {
+      from = get_uint (&arg);
+      if (end <= from)
+       error (_("'%u' is out of range."), from);
+
+      arg = skip_spaces (arg);
+      if (*arg == ',')
+       {
+         arg = skip_spaces (++arg);
+
+         if (*arg == '+')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             if (end - from < size)
+               size = end - from;
+             to = from + size;
+           }
+         else if (*arg == '-')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             /* Include the packet given as first argument.  */
+             from += 1;
+             to = from;
+
+             if (to - begin < size)
+               size = to - begin;
+             from = to - size;
+           }
+         else
+           {
+             to = get_uint (&arg);
+
+             /* Include the packet at the second argument and silently
+                truncate the range.  */
+             if (to < end)
+               to += 1;
+             else
+               to = end;
+
+             no_chunk (arg);
+           }
+       }
+      else
+       {
+         no_chunk (arg);
+
+         if (end - from < size)
+           size = end - from;
+         to = from + size;
+       }
+
+      dont_repeat ();
+    }
+
+  btrace_maint_print_packets (btinfo, from, to);
+}
+
+/* The "maintenance btrace clear-packet-history" command.  */
+
+static void
+maint_btrace_clear_packet_history_cmd (char *args, int from_tty)
+{
+  struct btrace_thread_info *btinfo;
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp == NULL)
+    error (_("No thread."));
+
+  btinfo = &tp->btrace;
+
+  /* Must clear the maint data before - it depends on BTINFO->DATA.  */
+  btrace_maint_clear (btinfo);
+  btrace_data_clear (&btinfo->data);
+}
+
+/* The "maintenance btrace clear" command.  */
+
+static void
+maint_btrace_clear_cmd (char *args, int from_tty)
+{
+  struct btrace_thread_info *btinfo;
+  struct thread_info *tp;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp == NULL)
+    error (_("No thread."));
+
+  btrace_clear (tp);
+}
+
+/* The "maintenance btrace" command.  */
+
+static void
+maint_btrace_cmd (char *args, int from_tty)
+{
+  help_list (maint_btrace_cmdlist, "maintenance btrace ", all_commands,
+            gdb_stdout);
+}
+
+/* The "maintenance set btrace" command.  */
+
+static void
+maint_btrace_set_cmd (char *args, int from_tty)
+{
+  help_list (maint_btrace_set_cmdlist, "maintenance set btrace ", all_commands,
+            gdb_stdout);
+}
+
+/* The "maintenance show btrace" command.  */
+
+static void
+maint_btrace_show_cmd (char *args, int from_tty)
+{
+  help_list (maint_btrace_show_cmdlist, "maintenance show btrace ",
+            all_commands, gdb_stdout);
+}
+
+/* The "maintenance set btrace pt" command.  */
+
+static void
+maint_btrace_pt_set_cmd (char *args, int from_tty)
+{
+  help_list (maint_btrace_pt_set_cmdlist, "maintenance set btrace pt ",
+            all_commands, gdb_stdout);
+}
+
+/* The "maintenance show btrace pt" command.  */
+
+static void
+maint_btrace_pt_show_cmd (char *args, int from_tty)
+{
+  help_list (maint_btrace_pt_show_cmdlist, "maintenance show btrace pt ",
+            all_commands, gdb_stdout);
+}
+
+/* The "maintenance info btrace" command.  */
+
+static void
+maint_info_btrace_cmd (char *args, int from_tty)
+{
+  struct btrace_thread_info *btinfo;
+  struct thread_info *tp;
+  const struct btrace_config *conf;
+
+  if (args != NULL && *args != 0)
+    error (_("Invalid argument."));
+
+  tp = find_thread_ptid (inferior_ptid);
+  if (tp == NULL)
+    error (_("No thread."));
+
+  btinfo = &tp->btrace;
+
+  conf = btrace_conf (btinfo);
+  if (conf == NULL)
+    error (_("No btrace configuration."));
+
+  printf_unfiltered (_("Format: %s.\n"),
+                    btrace_format_string (conf->format));
+
+  switch (conf->format)
+    {
+    default:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      printf_unfiltered (_("Number of packets: %u.\n"),
+                        VEC_length (btrace_block_s,
+                                    btinfo->data.variant.bts.blocks));
+      break;
+
+#if defined (HAVE_LIBIPT)
+    case BTRACE_FORMAT_PT:
+      {
+       struct pt_version version;
+
+       version = pt_library_version ();
+       printf_unfiltered (_("Version: %u.%u.%u%s.\n"), version.major,
+                          version.minor, version.build,
+                          version.ext != NULL ? version.ext : "");
+
+       btrace_maint_update_pt_packets (btinfo);
+       printf_unfiltered (_("Number of packets: %u.\n"),
+                          VEC_length (btrace_pt_packet_s,
+                                      btinfo->maint.variant.pt.packets));
+      }
+      break;
+#endif /* defined (HAVE_LIBIPT)  */
+    }
+}
+
+/* The "maint show btrace pt skip-pad" show value function. */
+
+static void
+show_maint_btrace_pt_skip_pad  (struct ui_file *file, int from_tty,
+                                 struct cmd_list_element *c,
+                                 const char *value)
+{
+  fprintf_filtered (file, _("Skip PAD packets is %s.\n"), value);
+}
+
+
+/* Initialize btrace maintenance commands.  */
+
+void _initialize_btrace (void);
+void
+_initialize_btrace (void)
+{
+  add_cmd ("btrace", class_maintenance, maint_info_btrace_cmd,
+          _("Info about branch tracing data."), &maintenanceinfolist);
+
+  add_prefix_cmd ("btrace", class_maintenance, maint_btrace_cmd,
+                 _("Branch tracing maintenance commands."),
+                 &maint_btrace_cmdlist, "maintenance btrace ",
+                 0, &maintenancelist);
+
+  add_prefix_cmd ("btrace", class_maintenance, maint_btrace_set_cmd, _("\
+Set branch tracing specific variables."),
+                  &maint_btrace_set_cmdlist, "maintenance set btrace ",
+                  0, &maintenance_set_cmdlist);
+
+  add_prefix_cmd ("pt", class_maintenance, maint_btrace_pt_set_cmd, _("\
+Set Intel Processor Trace specific variables."),
+                  &maint_btrace_pt_set_cmdlist, "maintenance set btrace pt ",
+                  0, &maint_btrace_set_cmdlist);
+
+  add_prefix_cmd ("btrace", class_maintenance, maint_btrace_show_cmd, _("\
+Show branch tracing specific variables."),
+                  &maint_btrace_show_cmdlist, "maintenance show btrace ",
+                  0, &maintenance_show_cmdlist);
+
+  add_prefix_cmd ("pt", class_maintenance, maint_btrace_pt_show_cmd, _("\
+Show Intel Processor Trace specific variables."),
+                  &maint_btrace_pt_show_cmdlist, "maintenance show btrace pt ",
+                  0, &maint_btrace_show_cmdlist);
+
+  add_setshow_boolean_cmd ("skip-pad", class_maintenance,
+                          &maint_btrace_pt_skip_pad, _("\
+Set whether PAD packets should be skipped in the btrace packet history."), _("\
+Show whether PAD packets should be skipped in the btrace packet history."),_("\
+When enabled, PAD packets are ignored in the btrace packet history."),
+                          NULL, show_maint_btrace_pt_skip_pad,
+                          &maint_btrace_pt_set_cmdlist,
+                          &maint_btrace_pt_show_cmdlist);
+
+  add_cmd ("packet-history", class_maintenance, maint_btrace_packet_history_cmd,
+          _("Print the raw branch tracing data.\n\
+With no argument, print ten more packets after the previous ten-line print.\n\
+With '-' as argument print ten packets before a previous ten-line print.\n\
+One argument specifies the starting packet of a ten-line print.\n\
+Two arguments with comma between specify starting and ending packets to \
+print.\n\
+Preceded with '+'/'-' the second argument specifies the distance from the \
+first.\n"),
+          &maint_btrace_cmdlist);
+
+  add_cmd ("clear-packet-history", class_maintenance,
+          maint_btrace_clear_packet_history_cmd,
+          _("Clears the branch tracing packet history.\n\
+Discards the raw branch tracing data but not the execution history data.\n\
+"),
+          &maint_btrace_cmdlist);
+
+  add_cmd ("clear", class_maintenance, maint_btrace_clear_cmd,
+          _("Clears the branch tracing data.\n\
+Discards the raw branch tracing data and the execution history data.\n\
+The next 'record' command will fetch the branch tracing data anew.\n\
+"),
+          &maint_btrace_cmdlist);
+
+}
This page took 0.059077 seconds and 4 git commands to generate.